البرمجة

حجز الذاكرة في لغة C

عامل sizeof وحجز مساحات التخزين في لغة C

تُعد لغة البرمجة C واحدة من أقدم وأشهر لغات البرمجة في العالم، وتتميز بقوتها الكبيرة في التعامل مع الموارد المادية للحاسوب بشكل مباشر. من بين أهم المفاهيم التي يجب على المبرمج فهمها جيداً في هذه اللغة هو كيفية إدارة الذاكرة، والتعامل مع حجز مساحات التخزين، بالإضافة إلى معرفة حجم البيانات المختلفة داخل البرنامج. يلعب عامل sizeof دوراً محورياً في هذا الجانب، فهو الأداة الأساسية التي تمكن المبرمج من معرفة حجم أي نوع بيانات أو متغير في الذاكرة، وهو ما يؤثر بشكل مباشر على حجز الذاكرة وعمليات التعامل مع المؤشرات والهيكلة.


مقدمة إلى مفهوم sizeof في لغة C

عامل sizeof هو عامل (operator) في لغة C يستخدم للحصول على حجم نوع بيانات أو متغير معين بوحدة البايت (byte). حجمه يُعبّر عن مقدار المساحة التي يحتاجها هذا النوع أو المتغير لتخزين البيانات في الذاكرة.

فعلى سبيل المثال، حجم نوع int قد يختلف بين أنظمة التشغيل أو المعالجات، ولذلك يعتمد المبرمج على sizeof ليتمكن من معرفة الحجم الدقيق أثناء وقت الترجمة (compile time).

الشكل العام لاستخدام sizeof يكون كالآتي:

c
sizeof(type)

أو

c
sizeof expression

حيث يمكن أن يكون type هو نوع بيانات مثل int، float، struct، أو يمكن أن يكون تعبيراً يمثل متغيراً.


أهمية معرفة حجم البيانات في لغة C

تختلف لغة C عن اللغات عالية المستوى بكونها لغة منخفضة المستوى نسبياً، تسمح بالوصول المباشر إلى الذاكرة، وهو ما يجعل إدارة حجم البيانات أمراً جوهرياً. تحديد حجم نوع البيانات يسهم في:

  • حجز الذاكرة بشكل صحيح: عندما يحتاج البرنامج إلى حجز مساحة ذاكرة ديناميكية (باستخدام malloc أو دوال أخرى)، يجب تحديد الحجم الدقيق للبيانات المراد تخزينها.

  • تحسين الأداء: فهم حجم البيانات يساعد في تقليل هدر الذاكرة، خصوصاً في الأجهزة ذات الموارد المحدودة.

  • ضمان سلامة البيانات: معرفة الحجم الحقيقي يمنع الأخطاء الناتجة عن تجاوز حدود الذاكرة (buffer overflow).

  • التوافقية بين الأنظمة: استخدام sizeof يساعد على كتابة برامج محمولة بين أنظمة تشغيل مختلفة، لأن أحجام أنواع البيانات قد تختلف بين بيئة وأخرى.


القيم النموذجية لحجم البيانات الأساسية

الأحجام النموذجية لأنواع البيانات الأساسية في لغة C تختلف بين الأنظمة والمعالجات، ولكن يمكن عرض القيم الشائعة على الأجهزة الحديثة 32-بت و64-بت:

نوع البيانات الحجم النموذجي (بايت)
char 1
short 2
int 4
long 4 أو 8 (حسب النظام)
long long 8
float 4
double 8
long double 8 أو 16
مؤشرات (pointers) 4 أو 8 (حسب النظام)

يلاحظ أن sizeof(char) ثابت دائماً ويساوي 1 بايت لأنه الوحدة الأساسية في الذاكرة.


كيفية استخدام sizeof مع المتغيرات والأنواع

يمكن استخدام sizeof بطريقتين رئيسيتين: على نوع بيانات أو على متغير.

استخدام sizeof مع النوع

c
printf("Size of int: %zu bytes\n", sizeof(int)); printf("Size of double: %zu bytes\n", sizeof(double));

استخدام sizeof مع المتغير

c
int a; double b; printf("Size of variable a: %zu bytes\n", sizeof a); printf("Size of variable b: %zu bytes\n", sizeof b);

يلاحظ أن الأقواس () اختيارية عند استخدام sizeof مع متغير، ولكنها ضرورية مع الأنواع.


استخدام sizeof مع المصفوفات

عند التعامل مع المصفوفات (Arrays)، فإن sizeof يعيد الحجم الكامل للمصفوفة بالكامل، أي عدد العناصر مضروباً في حجم كل عنصر.

c
int arr[10]; printf("Size of array: %zu bytes\n", sizeof(arr)); // 10 * sizeof(int)

لحساب عدد العناصر في المصفوفة باستخدام sizeof:

c
size_t num_elements = sizeof(arr) / sizeof(arr[0]); printf("Number of elements: %zu\n", num_elements);

هذا الاستخدام شائع جداً في لغة C لتجنب الأخطاء المتعلقة بتجاوز حدود المصفوفة.


sizeof مع الهياكل (structs)

الهياكل (structs) هي مجموعات من أنواع بيانات مختلفة تُجمع تحت اسم واحد. عند استخدام sizeof على هيكل، يتم إرجاع الحجم الكلي للهيكل مع الأخذ بعين الاعتبار التحاذي (alignment) والفجوات التي قد تضيفها المعالجات لضمان توافق البيانات مع بنية الذاكرة.

مثال على هيكل:

c
struct Person { char name[50]; int age; double height; };

يمكن معرفة حجم الهيكل كاملاً:

c
printf("Size of struct Person: %zu bytes\n", sizeof(struct Person));

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


الحجز الديناميكي لمساحات التخزين

في لغة C، يمكن حجز مساحات تخزين بشكل ديناميكي باستخدام دوال مثل malloc، calloc، وrealloc. هذه الدوال تعتمد بشكل مباشر على معرفة حجم البيانات لتخصيص كمية مناسبة من الذاكرة.

دالة malloc

malloc تحجز مساحة غير مهيأة (غير مملوءة بأي قيمة) بحجم معين بالبايت:

c
int *ptr = (int*) malloc(10 * sizeof(int));

في المثال أعلاه، يتم حجز مساحة كافية لعشرة أعداد صحيحة. استخدام sizeof(int) يضمن الحجز الصحيح على أي منصة.

دالة calloc

calloc تحجز مساحة كافية لعدد معين من العناصر وتقوم بمبادلة الذاكرة إلى الصفر:

c
int *ptr = (int*) calloc(10, sizeof(int));

هذه الدالة مهمة في حال الحاجة إلى مساحات مهيأة مسبقاً.

دالة realloc

تُستخدم لإعادة تخصيص الذاكرة لمؤشر معين:

c
ptr = (int*) realloc(ptr, 20 * sizeof(int));

التحاذي (Alignment) وأثره على sizeof

التحاذي هو مفهوم يشير إلى ترتيب البيانات في الذاكرة بحيث تبدأ عند عناوين تتوافق مع حجمها. فمثلاً، عادة ما تبدأ متغيرات من نوع int عند عناوين تكون من مضاعفات 4 بايت.

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

مثال على تأثير التحاذي:

c
struct Example { char c; int i; };

من المتوقع أن يكون الحجم 5 بايت (1 للـ char و4 للـ int)، لكن غالباً ما سيكون الحجم 8 بايت بسبب وجود فجوات لتوافق المعالجة.


الفرق بين sizeof وstrlen

في بعض الأحيان يحدث لبس بين عامل sizeof ودالة strlen. كلاهما يستخدم لقياس الحجم، لكن لكل منهما وظيفة مختلفة.

  • sizeof تعيد حجم المتغير أو النوع في الذاكرة بوحدة البايت، وتُعرف أثناء وقت الترجمة (مع استثناء بعض الحالات).

  • strlen تعيد عدد الأحرف في سلسلة نصية (string) حتى قبل العلامة النهائية \0، وتُحسب أثناء وقت التشغيل.

مثال:

c
char str[] = "Hello"; printf("sizeof: %zu\n", sizeof(str)); // 6 bytes (5 أحرف + '\0') printf("strlen: %zu\n", strlen(str)); // 5 أحرف فقط

sizeof وأنواع المؤشرات (Pointers)

عند استخدام sizeof على المؤشرات، فإنه يعيد حجم المؤشر نفسه، وليس حجم البيانات التي يشير إليها.

هذا الحجم يعتمد على نظام التشغيل والمعمارية (32-بت أو 64-بت):

  • على نظام 32-بت، حجم المؤشر غالباً ما يكون 4 بايت.

  • على نظام 64-بت، حجم المؤشر غالباً ما يكون 8 بايت.

مثال:

c
int *p; printf("Size of pointer: %zu\n", sizeof(p)); printf("Size of int pointed by p: %zu\n", sizeof(*p));

أهمية عامل sizeof في البرمجة الآمنة

الاعتماد على sizeof في العمليات المتعلقة بحجز الذاكرة، نسخ البيانات (باستخدام memcpy مثلاً)، وعمليات الإدخال/الإخراج، يساهم في تقليل الأخطاء التي قد تسببها التغييرات في حجم البيانات عبر الأنظمة أو تحديثات الكود.

مثال شائع هو:

c
memcpy(dest, src, sizeof(src));

بدلاً من كتابة حجم محدد ثابت، مما يجعل الكود أكثر مرونة وقابلية للصيانة.


خلاصة تقنية

  • sizeof هو عامل مهم جداً في لغة C لتحديد حجم الأنواع والمتغيرات في الذاكرة.

  • يتم التعبير عن الحجم بوحدة البايت.

  • يستخدم sizeof في الحجز الديناميكي للذاكرة باستخدام دوال مثل malloc و calloc.

  • يجب الانتباه للتحاذي الذي قد يغير الحجم الظاهر للهياكل.

  • الحجم الذي يرجعه sizeof يختلف بين الأنظمة (32-بت و64-بت).

  • لا يختص sizeof بحجم المحتوى الفعلي للنصوص أو الحاويات، بل بحجم نوع البيانات أو المتغير.

  • يعد استخدام sizeof أساسياً في كتابة برامج محمولة وآمنة من حيث إدارة الذاكرة.


مراجع ومصادر

  • كتاب “The C Programming Language” لكيرنيغان وريتشي (K&R).

  • توثيق مكتبة C القياسية: cplusplus.com – sizeof


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