سؤال
لقد قرأت هذا عن حشوة الهيكل في C:http://bytes.com/topic/c/answers/543879-what-structure-paddingوكتبت هذا الكود بعد المقالة، ما الذي يجب أن يطبع حجم "struct Pad" مثل 16 بايت وحجم "struct Pad2" يجب أن يكون 12.-على ما اعتقد.لقد قمت بتجميع هذا الكود باستخدام gcc، بمستويات مختلفة من التحسين، حتى أن عامل التشغيل sizeof() يمنحني 16 بايت لكليهما.لماذا هو؟
هذه المعلومات ضرورية بالنسبة لي بسبب أجهزة PS3، حيث تعد حدود البايت واستغلال نقل dma الكامل أمرًا مهمًا:
#include <stdio.h>
#include <stdlib.h>
struct pad
{
char c1; // 1 byte
short s1; // 2 byte
short s2; // 2 byte
char c2; // 1 byte
long l1; // 4 byte
char c3; // 1 byte
};
struct pad2
{
long l1;
short s1;
short s2;
char c1;
char c2;
char c3;
};
int main(void)
{
struct pad P1;
printf("%d\n", sizeof(P1));
struct pad P2;
printf("%d\n", sizeof(P2));
return EXIT_SUCCESS;
}
المحلول
هناك حيلتان يمكن استخدامهما للتغلب على هذه المشكلة
استخدام التوجيه #Pragma Pack (1) ثم #Pragma Pack (POP) مثال:
#pragma pack(1) struct tight{ short element_1; int *element_2; }; #pragma pack(pop)
للتحقق مما إذا كانت أحجام البنيتين متماثلة أثناء التجميع، استخدم هذه الخدعة
char voidstr[(sizeof(struct1)==sizeof(struct2)) - 1]; //it will return error at compile time if this fail
نصائح أخرى
تتضمن كل الهياكل الخاصة بك أ long
, ، والذي يبدو أن النظام الأساسي الخاص بك يتطلب أن يكون على حدود أربعة بايت.يجب أن تكون البنية على الأقل محاذاة مثل العضو الأكثر محاذاة، لذلك يجب أن تكون محاذاة 4 بايت، ويجب أن يكون حجم البنية مضاعفًا لمحاذاتها في حالة دخولها إلى صفيف.
مطلوب حشوة إضافية لجعل long
محاذاة، وبالتالي فإن أصغر مضاعف للعدد 4 هو 16.
نصيحتين:
يمكنك حساب إزاحة الحقل
l1
بواسطةprintf("Offset of field %s is %d\n", "l1", offsetof(struct pad, l1);
للحصول على
offsetof
الماكرو سوف تحتاج إلى#include <stddef.h>
(شكرا كاف!).إذا كنت تريد حزم البيانات بأكبر قدر ممكن من الكثافة، استخدم
unsigned char[4]
بدلاً منlong
وunsigned char[2]
بدلاً منshort
, ، وقم بإجراء العمليات الحسابية للتحويل.
يحرر::ال sizeof(struct pad2)
يكون 12.يحتوي الكود الخاص بك على خطأ؛بناء P2
تم الإعلان عن النوع struct pad
.جرب هذا:
#define xx(T) printf("sizeof(" #T ") == %d\n", sizeof(T))
xx(struct pad);
xx(struct pad2);
ملاحظة.يجب أن أتوقف بالتأكيد عن محاولة الإجابة على أسئلة SO بعد منتصف الليل.
في PS3، لا اعتقد. استخدام __attribute__((aligned (16)))
، أو ما شابه ذلك. ليس فقط أنها لا تضمن أن بداية بنية سيتم محاذاة على حد الصحيح (إذا عالمية أو ثابت)، ومنصات أيضا هيكل إلى مضاعفات محاذاة الخاص المحدد.
والتعليمات البرمجية الخاصة بك لا يظهر ما كنت أعتقد أنه هو، لأنه يتم تعريف كل من P1 و P2 مع حالات وحة البنية. لم يتم استخدام البنية PAD2 من أي وقت مضى.
إذا قمت بتغيير تعريف P2 بحيث يكون بنية PAD2، دول مجلس التعاون الخليجي لا يقرر في الواقع لجعله حجم 12.
struct pad P1;
printf("%d\n", sizeof(P1));
struct pad P2;
printf("%d\n", sizeof(P2));
وP1 و P2 لها نفس نوع "وسادة البنية" ربما كنت ترغب في استخدام "PAD2 البنية" لP2.
تتوقع جميع وحدات المعالجة المركزية أن يتم تخزين أنواع البيانات المضمنة مثل (int، float،char،double) في الذاكرة عند حدودها الطبيعية، عند عنوان طولها. لذلك يتم إجراء حشوة البنية للوصول بشكل أسرع إلى البيانات من الذاكرة.على سبيل المثال ، إذا تم الإعلان عن INT ، فيجب أن يحدث في الذاكرة على مضاعف العنوان 4 ،
حجم int هو 4 بايت.
وبالمثل بالنسبة للمضاعفة، فهي موجودة في الذاكرة عند مضاعفات الرقم 8.
إذا تمت محاذاة الذاكرة بشكل صحيح، فيمكن أن تعمل وحدة المعالجة المركزية بشكل أسرع وتعمل بكفاءة.
لنفترض في الأمثلة التالية:
Sizeof(int)=4 بايت
حجم (تعويم) = 4 بايت
حجم (شار) = 1 بايت
العثور على التفاصيل على BoundsCheck