سؤال

لتوضيح سؤالي ، لنبدأ مع برنامج مثال:

#include <stdio.h>

#pragma pack(push,1)
struct cc {
    unsigned int a   :  3;  
    unsigned int b   : 16;
    unsigned int c   :  1;
    unsigned int d   :  1;
    unsigned int e   :  1;
    unsigned int f   :  1;
    unsigned int g   :  1;
    unsigned int h   :  1;
    unsigned int i   :  6;  
    unsigned int j   :  6;  
    unsigned int k   :  4;  
    unsigned int l   : 15;
};
#pragma pack(pop)

struct cc c;

int main(int argc, char **argv)

{   printf("%d\n",sizeof(c));
}

الناتج هو "8" ، وهذا يعني أن 56 بت (7 بايت) أريد أن يتم تعبئتها في 8 بايت ، على ما يبدو يضيع بايت كامل. فضولي حول كيفية وضع المترجم هذه في الذاكرة ، حاولت كتابة قيم محددة إلى &c, ، على سبيل المثال:

int main (int argc ، char ** argv)

{
unsigned long long int* pint = &c;
*pint = 0xFFFFFFFF;
printf("c.a = %d", c.a);
...
printf("c.l = %d", c.l);
}

كما هو متوقع ، على x86_64 باستخدام Visual Studio 2010 ، يحدث ما يلي:

*pint = 0x00000000 000000FF :

c[0].a = 7
c[0].b = 1
c[0].c = 1
c[0].d = 1
c[0].e = 1
c[0].f = 1
c[0].g = 0
c[0].h = 0
c[0].i = 0
c[0].j = 0
c[0].k = 0
c[0].l = 0

*pint = 0x00000000 0000FF00 :

c[0].a = 0
c[0].b = 0
c[0].c = 0
c[0].d = 0
c[0].e = 0
c[0].f = 0
c[0].g = 1
c[0].h = 127
c[0].i = 0
c[0].j = 0
c[0].k = 0
c[0].l = 0


*pint = 0x00000000 00FF0000 :

c[0].a = 0
c[0].b = 0
c[0].c = 0
c[0].d = 0
c[0].e = 0
c[0].f = 0
c[0].g = 0
c[0].h = 32640
c[0].i = 0
c[0].j = 0
c[0].k = 0
c[0].l = 0

إلخ.

ننسى قابلية النقل للحظة وافترض أنك تهتم بوحدة المعالجة المركزية واحدة ومجمول مترجم واحد وبيئة وقت تشغيل واحد. لماذا لا تستطيع VC ++ حزمة هذا الهيكل إلى 7 بايت؟ هل هو شيء طول الكلمة؟ ال مستندات MSDN على #pragma pack يقول: "ستكون محاذاة العضو على حدود إما مضاعفة N [1 في حالتي] أو مضاعف حجم العضو ، أيهما أصغر." هل يمكن لأي شخص أن يعطيني فكرة عن سبب حصولي على حجم 8 وليس 7؟

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

المحلول

يخصص MSVC ++ دائمًا وحدة ذاكرة على الأقل تتوافق مع النوع الذي استخدمته في مجال البت الخاص بك. لقد استخدمت unsigned int, ، وهذا يعني أن أ unsigned int يتم تخصيصه في البداية وآخر unsigned int يتم تخصيصه عندما يتم استنفاد أول واحد. لا توجد وسيلة لإجبار MSVC ++ على تقليم الجزء غير المستخدم من الثاني unsigned int.

في الأساس ، يفسر MSVC ++ الخاص بك unsigned int كوسيلة للتعبير عن متطلبات المحاذاة للهيكل بأكمله.

استخدم أنواعًا أصغر لحقول البتات الخاصة بك (unsigned short و unsigned char) وإعادة تجميع حقول البت بحيث تملأ الوحدة المخصصة تمامًا - وبهذه الطريقة يجب أن تكون قادرًا على حزم الأشياء بإحكام قدر الإمكان.

نصائح أخرى

يتم تخزين Bitfields في النوع الذي تحدده. بما أنك تستخدم unsigned int, ، ولن يتناسب مع واحد unsigned int ثم يجب على المترجم استخدام عدد صحيح ثانٍ وتخزين آخر 24 بت في هذا العدد صحيح الأخير.

حسنًا ، أنت تستخدم INT غير موقعة والتي تصادف أن تكون 32 بت في هذه الحالة. الحدود التالية (لتتناسب مع Bitfield) لـ int غير موقعة هي 64 بت => 8 بايت.

PST صحيح. ال أفراد يتم محاذاة على حدود 1 بايت ، (أو أصغر ، لأنها صغيرة). يبلغ حجم الهيكل العام 8 ، ويتم محاذاة على حدود 8 بايت. هذا يتوافق مع كل من المعيار و pack اختيار. لم تقل المستندات أبدًا أنه لن يكون هناك حشوة في النهاية.

لتوضيح آخر مثير للاهتمام يوضح ما يجري ، فكر في الحالة التي تريد أن تحزم فيها هيكلًا يعبر حدود النوع. على سبيل المثال

struct state {
    unsigned int cost     : 24; 
    unsigned int back     : 21; 
    unsigned int a        :  1; 
    unsigned int b        :  1; 
    unsigned int c        :  1;
};

لا يمكن تعبئة هذا الهيكل إلى 6 بايت باستخدام MSVC بقدر ما أعرف. ومع ذلك ، يمكننا الحصول على تأثير التعبئة المطلوب عن طريق تفكيك الحقلين الأولين:

struct state_packed {
    unsigned short cost_1   : 16; 
    unsigned char  cost_2   :  8;
    unsigned short back_1   : 16; 
    unsigned char  back_2   :  5;
    unsigned char  a        :  1; 
    unsigned char  b        :  1; 
    unsigned char  c        :  1; 
};

هذا يمكن أن يكون معبأة في 6 بايت. ومع ذلك ، فإن الوصول إلى مجال التكلفة الأصلي أمر محرج وقبيح للغاية. طريقة واحدة هي إلقاء مؤشر state_packed إلى بنية دمية متخصصة:

struct state_cost {
    unsigned int cost     : 24;
    unsigned int junk     :  8; 
};

state_packed    sc;
state_packed *p_sc = &sc;

sc.a = 1;
(*(struct state_cost *)p_sc).cost = 12345;
sc.b = 1;

إذا كان أي شخص يعرف طريقة أكثر أناقة للقيام بذلك ، أحب أن أعرف!

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