لماذا لا sizeof على البنية يساوي مجموع sizeof من كل الأعضاء ؟

StackOverflow https://stackoverflow.com/questions/119123

سؤال

لماذا sizeof مشغل العودة حجم أكبر على هيكل من إجمالي أحجام الهيكل الأعضاء ؟

هل كانت مفيدة؟

المحلول

هذا هو لأنه من الحشو إضافة إلى تلبية محاذاة القيود. هيكل البيانات المحاذاة تأثير على الأداء وصحة البرامج:

  • سوء الانحياز الوصول قد يكون من الصعب خطأ (في كثير من الأحيان SIGBUS).
  • سوء الانحياز الوصول قد يكون خطأ ناعمة.
    • إما تصحيح في أجهزة متواضعة الأداء التدهور.
    • أو تصحيحها عن طريق مضاهاة في البرنامج ، الوخيمة الأداء التدهور.
    • وبالإضافة إلى ذلك ، atomicity وغيرها التزامن-الضمانات قد تكون مكسورة ، مما يؤدي إلى أخطاء خفية.

هنا مثال باستخدام إعدادات نموذجية على معالج x86 (تستخدم 32 و 64 بت وسائط):

struct X
{
    short s; /* 2 bytes */
             /* 2 padding bytes */
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 3 padding bytes */
};

struct Y
{
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
    short s; /* 2 bytes */
};

struct Z
{
    int   i; /* 4 bytes */
    short s; /* 2 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
};

const int sizeX = sizeof(struct X); /* = 12 */
const int sizeY = sizeof(struct Y); /* = 8 */
const int sizeZ = sizeof(struct Z); /* = 8 */

واحد يمكن التقليل من حجم هياكل والفرز أعضاء من المحاذاة (فرز حسب الحجم يكفي في أنواع أساسية) (مثل هيكل Z في المثال أعلاه).

ملاحظة هامة:كل من C و C++ معايير الدولة أن هيكل المحاذاة تنفيذ محددة.ولذلك كل مترجم اختيار محاذاة البيانات بشكل مختلف ، مما أدى إلى مختلف البيانات غير متوافقة تخطيطات.لهذا السبب عند التعامل مع المكتبات التي سيتم استخدامها من قبل مختلف المجمعين ، من المهم أن نفهم كيف المجمعين محاذاة البيانات.بعض المجمعين قد سطر الأوامر الإعدادات و/أو الخاصة #pragma البيانات إلى تغيير هيكل محاذاة الإعدادات.

نصائح أخرى

التعبئة بايت محاذاة ، كما هو موضح في التعليمات هنا:

يفعل المحاذاة.العديد من المعالجات لا يمكن الوصول 2 - و 4-بايت كميات (مثلا ، رجات طويلة رجات) إذا كانوا محشورين في كل الذي الطريق.

افترض أن لديك هذا الهيكل:

struct {
    char a[3];
    short int b;
    long int c;
    char d[3];
};

الآن, قد تعتقد أنه يجب أن يكون من الممكن أن حزمة هذا هيكل في الذاكرة مثل هذا:

+-------+-------+-------+-------+
|           a           |   b   |
+-------+-------+-------+-------+
|   b   |           c           |
+-------+-------+-------+-------+
|   c   |           d           |
+-------+-------+-------+-------+

لكنه أسهل بكثير على المعالج إذا كان المترجم يرتب مثل هذا:

+-------+-------+-------+
|           a           |
+-------+-------+-------+
|       b       |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           |
+-------+-------+-------+

في معبأة النسخة, لاحظ كيف انها على الأقل قليلا من الصعب وأنت ترى كيف b و c المجالات التفاف حولها ؟ باختصار ، من الصعب المعالج أيضا.ولذلك ، فإن معظم المجمعين سوف pad الهيكل (كما لو إضافية غير مرئية الحقول) مثل هذا:

+-------+-------+-------+-------+
|           a           | pad1  |
+-------+-------+-------+-------+
|       b       |     pad2      |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           | pad3  |
+-------+-------+-------+-------+

إذا كنت تريد هيكل أن يكون حجم معين مع دول مجلس التعاون الخليجي على سبيل المثال استخدام __attribute__((packed)).

على ويندوز يمكنك تعيين محاذاة إلى بايت واحد عند استخدام cl.exe compier مع /Zp الخيار.

وعادة ما هو الأسهل بالنسبة إلى وحدة المعالجة المركزية إلى الوصول إلى البيانات التي يتم متعددة من 4 (أو 8) ، اعتمادا منصة أيضا على مترجم.

لذلك هو مسألة المحاذاة في الأساس.

تحتاج إلى أن يكون لديك أسباب وجيهة إلى تغييره.

هذا يمكن أن يكون بسبب بايت محاذاة الحشو بحيث هيكل يخرج إلى عدد وحدات البايت (أو الكلمات) بشأن النظام الأساسي الخاص بك.على سبيل المثال في ج على لينكس التالية 3 الهياكل:

#include "stdio.h"


struct oneInt {
  int x;
};

struct twoInts {
  int x;
  int y;
};

struct someBits {
  int x:2;
  int y:6;
};


int main (int argc, char** argv) {
  printf("oneInt=%zu\n",sizeof(struct oneInt));
  printf("twoInts=%zu\n",sizeof(struct twoInts));
  printf("someBits=%zu\n",sizeof(struct someBits));
  return 0;
}

أعضاء من الأحجام (بالبايت) هي 4 بايت (32 بت) ، 8 بايت (2x 32 بت) و 1 بايت (2+6 بت) على التوالي.البرنامج أعلاه (على لينكس باستخدام gcc) طباعة الأحجام 4, 8, 4 - أين هيكل آخر هو مبطن بحيث يكون كلمة واحدة (4 × 8 بت بايت على 32bit منصة).

oneInt=4
twoInts=8
someBits=4

انظر أيضا:

Microsoft Visual C:

http://msdn.microsoft.com/en-us/library/2e70t5y1%28v=vs.80%29.aspx

و دول مجلس التعاون الخليجي مطالبة التوافق مع مايكروسوفت المترجم.:

http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html

بالإضافة إلى الأجوبة السابقة ، يرجى ملاحظة أنه بغض النظر التعبئة والتغليف ، هناك أفراد-من أجل ضمان في C++.المجمعين قد (و بالتأكيد) إضافة الجدول الظاهري مؤشر هياكل قاعدة' أعضاء الهيكل.حتى وجود الجدول الظاهري لا تضمن معيار (الظاهري آلية التنفيذ لم يتم تحديد) وبالتالي يمكن أن نستنتج أن مثل هذه الضمانة من المستحيل فقط.

أنا متأكد تماما عضو في النظام هو مضمونة في ج, ولكن أنا لا أعتمد على ذلك ، عند كتابة عبر منصة أو عبر مترجم البرنامج.

حجم الهيكل هو أكبر من مجموع أجزائه بسبب ما يسمى التعبئة.خاصة المعالج له فضل حجم البيانات التي تعمل مع.معظم المعالجات الحديثة' حجم المفضل إذا كان 32 بت (4 بايت).الوصول إلى الذاكرة عندما تكون البيانات على هذا النوع من الحدود هو أكثر كفاءة من الأشياء التي تمتد من أن حجم الحدود.

على سبيل المثال.النظر في بنية بسيطة:

struct myStruct
{
   int a;
   char b;
   int c;
} data;

إذا كان الجهاز 32 بت آلة البيانات الانحياز على 32 بت الحدود ، ونحن نرى مشكلة فورية (بافتراض عدم وجود هيكل التوافق).في هذا المثال, دعونا نفترض أن هيكل البيانات يبدأ في عنوان 1024 (0x400 علما أن أدنى 2 بت الصفر ، وبالتالي فإن البيانات تتوافق مع 32 بت الحدود).الوصول إلى البيانات.سوف تعمل بشكل جيد لأنه يبدأ على الحدود 0x400.الوصول إلى البيانات.ب أيضا تعمل بشكل جيد, لأنه في عنوان 0x404 - آخر 32 بت الحدود.ولكن محاذاتها هيكل من شأنه أن يضع البيانات.ج في عنوان 0x405.4 بايت من البيانات.ج في 0x405, 0x406, 0x407, 0x408.على 32 بت آلة النظام قراءة البيانات.ج أثناء أحد دورة الذاكرة ، ولكن سوف تحصل فقط على 3 من 4 بايت (4 بايت على القادم الحدود).لذا فإن النظام سوف تضطر إلى القيام الثاني ذاكرة الوصول إلى الحصول على 4 بايت ،

إذا بدلا من وضع البيانات.ج في عنوان 0x405, مترجم مبطن هيكل من 3 بايت ووضع البيانات.ج في عنوان 0x408 ، ثم النظام سوف تحتاج فقط 1 دورة لقراءة البيانات, قطع وصول الوقت إلى أن عناصر البيانات بنسبة 50%.الحشو مقايضة الذاكرة كفاءة كفاءة التصنيع.بالنظر إلى أن أجهزة الكمبيوتر يمكن أن يكون مبالغ ضخمة من الذاكرة (العديد من غيغابايت) ، المجمعين تشعر أن المبادلة (السرعة على حجم) هو معقول.

للأسف هذه المشكلة يصبح القاتل عند محاولة إرسال الهياكل عبر الشبكة أو حتى كتابة البيانات الثنائية إلى ملف ثنائي.الحشو تدرج بين عناصر الهيكل أو فئة يمكن أن يعطل البيانات التي يتم إرسالها إلى الملف أو الشبكة.من أجل كتابة رمز المحمولة (واحد التي سوف تذهب إلى عدة مختلفة المجمعين) ، ربما سيكون لديك الوصول إلى كل عنصر من عناصر هيكل منفصل لضمان حسن "التعبئة".

من ناحية أخرى, مختلفة المجمعين لديهم قدرات مختلفة لإدارة البيانات هيكل التعبئة.على سبيل المثال ، في Visual C/C++ مترجم يدعم #pragma حزمة الأوامر.هذا سوف يسمح لك لضبط البيانات التعبئة و المحاذاة.

على سبيل المثال:

#pragma pack 1
struct MyStruct
{
    int a;
    char b;
    int c;
    short d;
} myData;

I = sizeof(myData);

أنا الآن يجب أن يكون طول 11.دون pragma, أنا يمكن أن يكون أي شيء من 11 إلى 14 (بعض الأنظمة بقدر 32) ، اعتمادا على الافتراضي التعبئة من المترجم.

فإنه يمكن القيام بذلك إذا كنت قد ضمنا أو صراحة المواءمة بين البنية.البنية التي تتماشى 4 سوف يكون دائما متعددة من 4 بايت حتى لو كان حجم أعضائها أن يكون شيء من ذلك ليس متعددة من 4 بايت.

أيضا مكتبة قد جمعت تحت x86 مع 32 بت رجات و قد تكون المقارنة بين مكوناته على 64 بت عملية من شأنها أن تعطيك نتيجة مختلفة إذا كنت تفعل هذا من جهة.

C99 N1256 القياسية مشروع

http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf

6.5.3.4 بحجم المشغل:

3 عند تطبيقها إلى المعامل التي لديها هيكل أو اتحاد النوع ، والنتيجة هي إجمالي عدد وحدات البايت في مثل هذا الكائن ، بما في ذلك الداخلية زائدة الحشو.

6.7.2.1 هيكل الاتحاد محددات:

13 ...قد يكون هناك لم يكشف عن اسمه الحشو داخل بنية الكائن ، ولكن ليس في بدايتها.

و:

15 قد يكون هناك لم يذكر اسمه الحشو في نهاية الهيكل أو الاتحاد.

الجديد C99 مجموعة مرنة الأعضاء ميزة (struct S {int is[];};) قد تؤثر أيضا على الحشو:

16 كحالة خاصة العنصر الأخير من الهيكل مع أكثر من واحد اسمه الأعضاء قد غير مكتمل مجموعة نوع ؛ وهذا ما يسمى مجموعة مرنة الأعضاء.في معظم الحالات ، مرنة عضو الصفيف هو تجاهلها.ولا سيما حجم الهيكل كما لو مجموعة مرنة الأعضاء حذفت إلا أنه قد يكون أكثر زائدة من الحشو إغفال يعني.

المرفق ياء قابلية القضايا وتكرر:

التالية هي غير محدد:...

  • قيمة بايت الحشو عند تخزين القيم في الهياكل أو النقابات (6.2.6.1)

C++11 N3337 القياسية مشروع

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf

5.3.3 Sizeof:

2 عند تطبيقها إلى فئة ، والنتيجة هي عدد وحدات البايت في كائن من تلك الفئة بما في ذلك أي الحشو المطلوب وضع الكائنات من هذا النوع في صفيف.

9.2 أعضاء الفئة:

مؤشر إلى معيار تخطيط البنية كائن مناسب تحويلها باستخدام reinterpret_cast ، ويشير إلى الأولي الأعضاء (أو إذا كان ذلك العضو قليلا الميدان ، ثم إلى الوحدة التي يتواجد) والعكس بالعكس.[ ملاحظة:هناك قد يكون اسمه الحشو داخل القياسية-تخطيط البنية كائن ، ولكن ليس في بدايتها ، ضرورية لتحقيق المحاذاة المناسبة.— end note ]

أنا أعرف فقط ما يكفي C++ لفهم ملاحظة :-)

بالإضافة إلى إجابات أخرى ، البنية يمكن (ولكن عادة لا) لديهم وظائف افتراضية, في هذه الحالة حجم البنية سوف تشمل أيضا مساحة vtbl.

لغة C يترك المترجم بعض الحرية عن موقع العناصر الهيكلية في الذاكرة:

  • الذاكرة ثقوب قد تظهر بين أي عنصرين ، و بعد آخر عنصر.كان يرجع ذلك إلى حقيقة أن بعض أنواع الكائنات على الكمبيوتر الهدف قد يكون محدودا حدود معالجة
  • "الذاكرة ثقوب" بحجم تضمينها في نتيجة sizeof المشغل.بحجم فقط لا يتضمن حجم مجموعة مرنة ، والذي يتوفر في C/C++
  • بعض تطبيقات اللغة التي تسمح لك للسيطرة على الذاكرة تخطيط الهياكل من خلال pragma و خيارات برنامج التحويل البرمجي

لغة C يوفر بعض الضمانات مبرمج من عناصر تخطيط في هيكل:

  • المجمعين المطلوبة لتعيين تسلسل عناصر زيادة عناوين الذاكرة
  • عنوان العنصر الأول يتزامن مع بدء معالجة هيكل
  • لم يذكر اسمه بعض الحقول قد تكون مدرجة في هيكل المطلوبة عنوان التحالفات من العناصر المجاورة

المشاكل المتعلقة عناصر المحاذاة:

  • أجهزة كمبيوتر مختلفة خط حواف الأجسام بطرق مختلفة
  • القيود المختلفة على عرض حقل بت
  • أجهزة الكمبيوتر تختلف على كيفية تخزين بايت في كلمة (إنتل أجهزة 80 x 86 و موتورولا 68000)

كيفية محاذاة يعمل:

  • حجم المحتلة من قبل هيكل يتم احتساب حجم الانحياز عنصر واحد من مجموعة من هذه الهياكل.بنية أن حتى أول عنصر التالي هيكل لا تنتهك متطلبات التوافق

p.s معلومات أكثر تفصيلا متاحة هنا:"Samuel P. هاربيسون الرجل L. ستيل ج مرجع ، (5.6.2 - 5.6.7)"

الفكرة هي أن سرعة ذاكرة التخزين المؤقت الاعتبارات المعاملات يجب أن يكون قراءة من عناوين الانحياز إلى حجمها الطبيعي.لجعل هذا يحدث, مترجم منصات هيكل الأعضاء الأعضاء التالية أو التالية البنية سيتم الانحياز.

struct pixel {
    unsigned char red;   // 0
    unsigned char green; // 1
    unsigned int alpha;  // 4 (gotta skip to an aligned offset)
    unsigned char blue;  // 8 (then skip 9 10 11)
};

// next offset: 12

X86 العمارة كانت دائما قادرة على جلب المنحرفة عناوين.ومع ذلك ، فإنه أبطأ عند اختلال التداخل مختلفين ذاكرة التخزين المؤقت خطوط ، ثم تطرد اثنين من ذاكرة التخزين المؤقت الخطوط عندما الانحياز الوصول فقط طرد واحد.

بعض أبنية فعلا فخ على المنحرفة يقرأ و يكتب و الإصدارات في وقت مبكر من بنية ARM (التي تطورت إلى كل من اليوم النقالة وحدات المعالجة المركزية) ...حسنا, هم في الواقع مجرد إرجاع البيانات السيئة على تلك.(أنها تجاهلت الترتيب المنخفض بت.)

وأخيرا نلاحظ أن ذاكرة التخزين المؤقت الخطوط يمكن أن تكون كبيرة بشكل تعسفي ، و المترجم لا تحاول تخمين في تلك أو جعل مساحة-vs-سرعة المقايضة.بدلا من محاذاة القرارات هي جزء من أبي و تمثل الحد الأدنى من التوافق الذي سيكون في نهاية المطاف بالتساوي تملأ ذاكرة التخزين المؤقت الخط.

TL;DR: محاذاة هو المهم.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top