تخصيص الذاكرة الديناميكي في لغة C: malloc()، calloc()
⚡ ملخص ذكي
يقوم تخصيص الذاكرة الديناميكي في لغة C بحجز وتحرير ذاكرة الكومة في وقت التشغيل باستخدام وظائف malloc() و calloc() و realloc() و free() من مكتبة stdlib.h، مما يسمح للبرامج بتحديد حجم المصفوفات والهياكل بمرونة عندما تكون المتطلبات غير معروفة قبل التنفيذ.

كيف تعمل إدارة الذاكرة في لغة C؟
عندما تقوم بتعريف متغير باستخدام نوع بيانات أساسي، يقوم مترجم لغة C تلقائيًا بتخصيص مساحة ذاكرة للمتغير في مجموعة من الذاكرة تسمى المكدس.
على سبيل المثال، يشغل متغير من نوع float عادةً 4 بايتات (بحسب النظام الأساسي) عند تعريفه. يمكننا التحقق من هذه المعلومة باستخدام عامل sizeof كما هو موضح في المثال أدناه.
#include <stdio.h> int main() { float x; printf("The size of float is %d bytes", sizeof(x)); return 0;}
سوف يكون الإخراج:
The size of float is 4 bytes
أيضًا، يتم تخصيص مصفوفة ذات حجم محدد في كتل متجاورة من الذاكرة، كل كتلة لها حجم عنصر واحد:
#include <stdio.h> int main() { float arr[10]; printf("The size of the float array with 10 element is %d", sizeof(arr)); return 0;}
النتيجه هي:
The size of the float array with 10 element is 40
كما تعلمنا حتى الآن، عند تعريف نوع بيانات أساسي أو مصفوفة، تتم إدارة الذاكرة تلقائيًا. مع ذلك، توجد عملية لتخصيص الذاكرة في لغة C تسمح لك بتنفيذ برنامج لا يُحدد فيه حجم المصفوفة إلا عند تشغيل البرنامج (وقت التشغيل). تُسمى هذه العملية "تخصيص الذاكرة الديناميكي".
تخصيص الذاكرة الديناميكية في C
تخصيص الذاكرة الديناميكي هو عملية تخصيص وتحرير يدوية للذاكرة وفقًا لاحتياجات البرمجة. تُدار الذاكرة الديناميكية وتُخدَم باستخدام مؤشرات تُشير إلى مساحة الذاكرة المُخصصة حديثًا في منطقة تُسمى الكومة.
يمكنك الآن إنشاء وتدمير مجموعة من العناصر ديناميكيًا في وقت التشغيل دون أي مشاكل. لتلخيص ذلك، تستخدم إدارة الذاكرة التلقائية المكدس، ويستخدم تخصيص الذاكرة الديناميكي C الكومة.
ال تحتوي المكتبة على وظائف مسؤولة عن إدارة الذاكرة الديناميكية.
| الوظيفة | الهدف |
|---|---|
| مالوك () | يخصص الذاكرة بالحجم المطلوب ويعيد المؤشر إلى البايت الأول المساحة المخصصة. |
| calloc () | يخصص المساحة لعناصر المصفوفة. تهيئة العناصر إلى الصفر وإرجاع المؤشر إلى الذاكرة. |
| realloc () | يتم استخدامه لتعديل حجم مساحة الذاكرة المخصصة مسبقًا. |
| حر() | يحرر أو يفرغ مساحة الذاكرة المخصصة مسبقًا. |
دعونا نناقش الوظائف المذكورة أعلاه مع تطبيقها
وظيفة malloc () في C
تشير الدالة C malloc() إلى تخصيص الذاكرة. إنها وظيفة تُستخدم لتخصيص كتلة من الذاكرة ديناميكيًا. يقوم بحجز مساحة ذاكرة بالحجم المحدد ويعيد المؤشر الفارغ الذي يشير إلى موقع الذاكرة. المؤشر الذي تم إرجاعه عادة ما يكون من النوع الفارغ. هذا يعني أنه يمكننا تعيين وظيفة C malloc () لأي مؤشر.
بناء جملة الدالة malloc():
ptr = (cast_type *) malloc (byte_size);
هنا،
- ptr هو مؤشر cast_type.
- تقوم الدالة C malloc() بإرجاع مؤشر إلى الذاكرة المخصصة لـ byte_size.
مثال على malloc ():
Example: ptr = (int *) malloc (50)
عند تنفيذ هذا البيان بنجاح، يتم حجز مساحة ذاكرة قدرها 50 بايت. يتم تعيين عنوان البايت الأول من المساحة المحجوزة للمؤشر ptr من النوع int.
النظر في مثال آخر:
#include <stdlib.h> int main(){ int *ptr; ptr = malloc(15 * sizeof(*ptr)); /* a block of 15 integers */ if (ptr != NULL) { *(ptr + 5) = 480; /* assign 480 to sixth integer */ printf("Value of the 6th integer is %d",*(ptr + 5)); } }
الإخراج:
Value of the 6th integer is 480
- لاحظ أنه تم استخدام sizeof(*ptr) بدلاً من sizeof(int) لجعل الكود أكثر قوة عندما يتم تحويل تعريف *ptr إلى نوع بيانات مختلف لاحقًا.
- قد يفشل التخصيص إذا كانت الذاكرة غير كافية. في هذه الحالة، تقوم بإرجاع مؤشر NULL. لذا، يجب عليك تضمين رمز للتحقق من وجود مؤشر NULL.
- ضع في اعتبارك أن الذاكرة المخصصة متجاورة ويمكن التعامل معها كمصفوفة. يمكننا استخدام حساب المؤشر للوصول إلى عناصر المصفوفة بدلاً من استخدام الأقواس [ ]. ننصح باستخدام + للإشارة إلى عناصر المصفوفة لأن استخدام الزيادة ++ أو += يغير العنوان المخزن بواسطة مؤشر.
يمكن أيضًا استخدام دالة Malloc() مع نوع بيانات الحرف بالإضافة إلى أنواع البيانات المعقدة مثل الهياكل.
وظيفة مجانية () في C
ذاكرة المتغيرات يتم إلغاء تخصيصها تلقائيًا في وقت الترجمة. في تخصيص الذاكرة الديناميكية، يجب عليك إلغاء تخصيص الذاكرة بشكل صريح. إذا لم يتم ذلك، فقد تواجه خطأ نفاد الذاكرة.
يتم استدعاء الدالة free() لتحرير/إلغاء تخصيص الذاكرة في لغة C. من خلال تحرير الذاكرة في برنامجك، فإنك تجعل المزيد منها متاحًا للاستخدام لاحقًا.
فمثلا:
#include <stdio.h> int main() { int* ptr = malloc(10 * sizeof(*ptr)); if (ptr != NULL){ *(ptr + 2) = 50; printf("Value of the 2nd integer is %d",*(ptr + 2)); } free(ptr); }
الإخراج:
Value of the 2nd integer is 50
وظيفة calloc() في C
تشير دالة C calloc() إلى التخصيص المتجاور. تُستخدم هذه الدالة لتخصيص كتل متعددة من الذاكرة. إنها دالة تخصيص ذاكرة ديناميكية تُستخدم لتخصيص الذاكرة لهياكل البيانات المعقدة مثل المصفوفات والهياكل.
يتم استخدام وظيفة Malloc() لتخصيص كتلة واحدة من مساحة الذاكرة بينما يتم استخدام calloc() في لغة C لتخصيص كتل متعددة من مساحة الذاكرة. كل كتلة مخصصة بواسطة الدالة calloc() لها نفس الحجم.
بناء جملة وظيفة calloc():
ptr = (cast_type *) calloc (n, size);
يتم استخدام العبارة أعلاه لتخصيص كتل الذاكرة n من نفس الحجم.
بعد تخصيص مساحة الذاكرة، تتم تهيئة كافة البايتات إلى الصفر.
يتم إرجاع المؤشر الموجود حاليًا عند البايت الأول من مساحة الذاكرة المخصصة.
عندما يكون هناك خطأ في تخصيص مساحة الذاكرة مثل نقص الذاكرة، يتم إرجاع مؤشر فارغ.
مثال على calloc():
البرنامج أدناه يحسب مجموع المتتابعة الحسابية.
#include <stdio.h> int main() { int i, * ptr, sum = 0; ptr = calloc(10, sizeof(int)); if (ptr == NULL) { printf("Error! memory not allocated."); exit(0); } printf("Building and calculating the sequence sum of the first 10 terms \ n "); for (i = 0; i < 10; ++i) { * (ptr + i) = i; sum += * (ptr + i); } printf("Sum = %d", sum); free(ptr); return 0; }
النتيجة:
Building and calculating the sequence sum of the first 10 terms
Sum = 45
calloc() vs. malloc(): الاختلافات الرئيسية
فيما يلي الفرق الرئيسي بين مالوك () مقابل كالوك () في C:
تعتبر دالة calloc() أكثر ملاءمة وكفاءة بشكل عام من دالة malloc(). وبينما تُستخدم كلتا الدالتين لتخصيص مساحة الذاكرة، يمكن لدالة calloc() تخصيص كتل متعددة في وقت واحد. ولا يتعين عليك طلب كتلة ذاكرة في كل مرة. تُستخدم دالة calloc() في هياكل البيانات المعقدة التي تتطلب مساحة ذاكرة أكبر.
تتم دائمًا تهيئة كتلة الذاكرة المخصصة بواسطة calloc() في C إلى الصفر بينما في الدالة malloc() في C، فإنها تحتوي دائمًا على قيمة غير صحيحة.
وظيفة realloc() في C
باستخدام دالة realloc() في لغة C، يمكنك زيادة حجم الذاكرة المخصصة مسبقًا. فهي توسّع الكتلة الحالية مع الحفاظ على محتواها الأصلي كما هو. وتعني realloc() في لغة C إعادة تخصيص الذاكرة.
يمكن أيضًا استخدام realloc() لتقليل حجم الذاكرة المخصصة مسبقًا.
بناء جملة وظيفة realloc():
ptr = realloc (ptr,newsize);
يخصص البيان أعلاه مساحة ذاكرة جديدة بحجم محدد في حجم الأخبار المتغير. بعد تنفيذ الوظيفة، سيتم إرجاع المؤشر إلى البايت الأول من كتلة الذاكرة. يمكن أن يكون الحجم الجديد أكبر أو أصغر من الذاكرة السابقة. لا يمكننا التأكد من أن الكتلة المخصصة حديثًا ستشير إلى نفس موقع كتلة الذاكرة السابقة. ستقوم هذه الوظيفة بنسخ كافة البيانات السابقة في المنطقة الجديدة. فهو يتأكد من أن البيانات ستبقى آمنة.
مثال على إعادة تخصيص ():
#include <stdio.h> int main () { char *ptr; ptr = (char *) malloc(10); strcpy(ptr, "Programming"); printf(" %s, Address = %u\n", ptr, ptr); ptr = (char *) realloc(ptr, 20); //ptr is reallocated with new size strcat(ptr, " In 'C'"); printf(" %s, Address = %u\n", ptr, ptr); free(ptr); return 0; }
عندما تؤدي realloc() في C إلى عملية غير ناجحة، فإنها تعيد مؤشرًا فارغًا، ويتم أيضًا تحرير البيانات السابقة.
المصفوفات الديناميكية في لغة C
تسمح المصفوفة الديناميكية في لغة C بزيادة عدد العناصر حسب الحاجة. تُستخدم المصفوفات الديناميكية في لغة C على نطاق واسع في خوارزميات علوم الكمبيوتر.
في البرنامج التالي، قمنا بإنشاء مصفوفة ديناميكية وتغيير حجمها في C
#include <stdio.h> int main() { int * arr_dynamic = NULL; int elements = 2, i; arr_dynamic = calloc(elements, sizeof(int)); //Array with 2 integer blocks for (i = 0; i < elements; i++) arr_dynamic[i] = i; for (i = 0; i < elements; i++) printf("arr_dynamic[%d]=%d\n", i, arr_dynamic[i]); elements = 4; arr_dynamic = realloc(arr_dynamic, elements * sizeof(int)); //reallocate 4 elements printf("After realloc\n"); for (i = 2; i < elements; i++) arr_dynamic[i] = i; for (i = 0; i < elements; i++) printf("arr_dynamic[%d]=%d\n", i, arr_dynamic[i]); free(arr_dynamic); }
نتيجة برنامج المصفوفة الديناميكية C على الشاشة:
arr_dynamic[0]=0 arr_dynamic[1]=1 After realloc arr_dynamic[0]=0 arr_dynamic[1]=1 arr_dynamic[2]=2 arr_dynamic[3]=3

