مستوى المقال: محترف — يفترض إنك بتكتب بلغة فيها بنى مثل C أو C++ أو Rust أو Go، وإنك بتتعامل مع مصفوفات كبيرة من الـ structs.
ليه الـ struct بياخد ذاكرة أكتر من مجموع حقوله؟ Struct Padding بالتفصيل
لو عندك مصفوفة فيها 100 مليون struct، ترتيب الحقول جوّه الـ struct لوحده ممكن يفرّق 800 ميجابايت في الرام من غير ما تغيّر بايت واحد من بياناتك. المقال ده بيوريك ليه بيحصل ده، وإزاي تقيسه، وإزاي تصلحه بإعادة ترتيب سطرين.
المشكلة باختصار
خد الـ struct ده: char a (1 بايت) + int b (4) + char c (1) + double d (8). مجموع الحقول 14 بايت. تتوقع الـ struct ياخد 14. اطبع sizeof هتلاقيه 24. فين الـ 10 بايت الزيادة؟ دي مش بياناتك، دي حشو (padding) المترجم بيحطه عشان يحافظ على المحاذاة.
ليه المترجم بيحط حشو؟ قاعدة المحاذاة (Alignment)
تخيّل رفّ كتب مقسوم لخانات، كل خانة 4 سنتيمتر. القاعدة: أي كتاب لازم يبدأ من أول خانة، مينفعش يبدأ من نص خانة. لو عندك كتاب صغير احتل نص خانة، الكتاب اللي بعده لازم يستنى لأول الخانة الجاية، والفراغ اللي في النص بيفضل ضايع. ده بالظبط اللي بيحصل في الذاكرة.
علميًا: المعالج بيقرا الذاكرة في وحدات محاذاة. الـ int حجمه 4 بايت ولازم عنوانه يكون من مضاعفات 4. الـ double حجمه 8 ولازم عنوانه من مضاعفات 8. المترجم لمّا يحط char a في العنوان 0، الـ int b اللي بعده مينفعش يقعد في العنوان 1، لازم يستنى لعنوان 4. فبيتحط حشو في 1 و2 و3. نفس الحكاية قبل الـ double. والـ struct نفسه حجمه لازم يكون من مضاعفات أكبر حقل فيه (8 هنا)، عشان لو عملت منه مصفوفة يفضل كل عنصر محاذي صح.
الافتراض هنا إنك على معمارية x86-64 بواجهة System V ABI (لينكس وmacOS). الأرقام بتختلف على معماريات تانية، بس المبدأ واحد.
قيس بنفسك: كود C بيطبع الحجم
مش محتاج تصدّقني. جرّب الكود ده بنفسك:
#include <stdio.h>
#include <stddef.h>
struct Bad { char a; int b; char c; double d; };
struct Good { double d; int b; char a; char c; };
int main(void) {
printf("Bad size = %zu\n", sizeof(struct Bad));
printf("Good size = %zu\n", sizeof(struct Good));
printf("Bad.b offset = %zu\n", offsetof(struct Bad, b));
printf("Bad.d offset = %zu\n", offsetof(struct Bad, d));
return 0;
}المخرج بيأكد الكلام: Bad بياخد 24 وGood بياخد 16. لاحظ إن b بادئ من العنوان 4 مش 1، وده الدليل المباشر على الحشو.
الحل: أعد ترتيب الحقول من الأكبر للأصغر
الترتيب مش عشوائي. رتّب حقولك تنازليًا حسب حجمها: الـ الأول، بعده الـ ، بعده الـ . كده كل حقل بيقع طبيعي في مكانه المحاذي من غير فجوات في النص، والحشو الوحيد اللي بيفضل هو في الآخر عشان حجم الـ struct يكمل لمضاعف 8.