سؤال

لدي خلفية C#. أنا مبتدئ كثيرًا في لغة منخفضة المستوى مثل C.

في C#، structيتم وضع ذاكرة بواسطة المترجم افتراضيًا. يمكن للمترجم إعادة ترتيب حقول البيانات أو PAD أجزاء إضافية بين الحقول ضمنيًا. لذلك ، اضطررت إلى تحديد بعض السمات الخاصة لتجاوز هذا السلوك للتخطيط الدقيق.

AFAIK ، C لا يعيد ترتيب أو محاذاة تخطيط الذاكرة من أ struct بشكل افتراضي. ومع ذلك ، سمعت أن هناك استثناء صغير يصعب العثور عليه.

ما هو سلوك تخطيط ذاكرة C؟ ماذا يجب إعادة ترتيب/محاذاة وليس؟

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

المحلول

في C ، يُسمح للمترجم بإملاء بعض المحاذاة لكل نوع بدائي. عادةً ما يكون المحاذاة هو حجم النوع. لكنها خاصة بالتنفيذ.

يتم تقديم بايت الحشو بحيث يتم محاذاة كل كائن بشكل صحيح. إعادة الترتيب غير مسموح بها.

ربما كل برنامج التحويل البرمجي الحديث عن بعد #pragma pack مما يسمح بالتحكم في الحشو ويتركه للبرنامج للامتثال لـ ABI. (إنه غير قياسي تمامًا ، رغم ذلك)

من C99 §6.7.2.1:

12 يتم محاذاة كل عضو من أعضاء المجال غير البني في هيكل أو كائن اتحاد بطريقة محددة التنفيذ مناسبة لنوعه.

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

نصائح أخرى

إنه خاص بالتنفيذ ، ولكن في الممارسة العملية القاعدة (في غياب #pragma pack أو ما شابه) هو:

  • يتم تخزين أعضاء الهيكل بالترتيب الذي تم الإعلان عنه. (هذا مطلوب بموجب معيار C99 ، كما ذكرنا هنا سابقًا.)
  • إذا لزم الأمر ، تتم إضافة الحشوة قبل كل عضو هيكل ، لضمان المحاذاة الصحيحة.
  • يتطلب كل نوع بدائي T محاذاة sizeof(T) بايت.

لذلك ، بالنظر إلى البنية التالية:

struct ST
{
   char ch1;
   short s;
   char ch2;
   long long ll;
   int i;
};
  • ch1 هو في الإزاحة 0
  • يتم إدراج بايت حشوة لمحاذاة ...
  • s في الإزاحة 2
  • ch2 هو في إزاحة 4 ، مباشرة بعد S
  • يتم إدخال 3 بايتات حشوة لمحاذاة ...
  • ll في الإزاحة 8
  • i هو في إزاحة 16 ، مباشرة بعد LL
  • تتم إضافة 4 بايت حشوة في النهاية بحيث يكون الهيكل الكلي مضاعفًا من 8 بايت. لقد راجعت هذا على نظام 64 بت: قد تسمح أنظمة 32 بت بهياكل أن يكون لها محاذاة 4 بايت.

لذا sizeof(ST) هو 24.

يمكن تخفيضه إلى 16 بايت عن طريق إعادة ترتيب الأعضاء لتجنب الحشو:

struct ST
{
   long long ll; // @ 0
   int i;        // @ 8
   short s;      // @ 12
   char ch1;     // @ 14
   char ch2;     // @ 15
} ST;

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

من مقال ويكيبيديا:

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

من 6.54.8 pragmas هيكل من وثائق دول مجلس التعاون الخليجي:

بالنسبة للتوافق مع مجمعات Microsoft Windows ، تدعم GCC مجموعة من توجيهات #Pragma التي تغير الحد الأقصى لمحاذاة أعضاء الهياكل (بخلاف حقول Bitfields ذات العرض الصفري) والنقابات والفئات المحددة لاحقًا. يجب دائمًا أن تكون قيمة N أدناه قوة صغيرة تبلغ قيمتها وتحدد المحاذاة الجديدة في البايتات.

  1. #pragma pack(n) ببساطة يضع المحاذاة الجديدة.
  2. #pragma pack() يحدد المحاذاة إلى تلك التي كانت سارية عند بدء التجميع (انظر أيضًا خيار سطر الأوامر -fpack -struct [=] انظر خيارات الكود الجنرال).
  3. #pragma pack(push[,n]) يدفع إعداد المحاذاة الحالي على مكدس داخلي ثم يقوم اختياريًا بتعيين المحاذاة الجديدة.
  4. #pragma pack(pop) يعيد إعداد المحاذاة إلى واحد تم حفظه في الجزء العلوي من المكدس الداخلي (ويزيل إدخال المكدس هذا). لاحظ أن #pragma pack([n]) لا يؤثر على هذا المكدس الداخلي. وبالتالي من الممكن أن يكون #pragma pack(push)تليها متعددة #pragma pack(n)حالات وينتهيها واحدة #pragma pack(pop).

بعض الأهداف ، على سبيل المثال I386 و PowerPC ، تدعم ms_struct #pragma الذي يضع هيكلًا كما هو موثق __attribute__ ((ms_struct)).

  1. #pragma ms_struct on يتحول على تخطيط الهياكل المعلنة.
  2. #pragma ms_struct off يطفئ التخطيط للهياكل المعلنة.
  3. #pragma ms_struct reset يعود إلى التصميم الافتراضي.
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top