منشور

التخصيص الديناميكي للذاكرة في لغة سي

شرح دالات malloc و calloc و free و realloc في لغة سي

بما إن لغة سي لغة هيكلية فإن لها قواعد ثابتة في البرمجة، ومن ذلك تغيير حجم مصفوفة. المصفوفة هي مجموعة من العناصر المخزنة بأماكن متصلة من الذاكرة (اي ليست بأماكن متقطعة - بأماكن مختلفة من الذاكرة).

مخطط الصنف للصراف الالي

كما ترى فإن حجم (طول) المصفوفة أعلاه هو 9. وهو ثابت في حالة المصفوفات. ولكن قد نحتاج إلى تغيير هذا الحجم في حالات مثل:

  • في حال كان هناك حاجة لإدخال خمسة عناصر فقط في المصفوفة، في هذه الحالة تكون العناصر الأربعة المتبقية والفارغة هي عبارة عن هدر للذاكرة ونحتاج لتخفيض حجم المصفوفة من 9 إلى 5.

  • في حال كانت جميع العناصر التسعة في المصفوفة ممتلئة وكنا نحتاج لإدخال 3 عناصر إضافيين. في هذه الحالة فإننا نحتاج لتوسيع حجم المصفوفة من 9 إلى 12.

عملية تغيير حجم المصفوفة في لغة سي تدعى “التخصيص الديناميكي للذاكرة” أو بالإنجليزية “Dynamic Memory Allocation” وديناميكي عموما يعني حركي أو متغير أي غير ثابت.

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

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

  1. ()malloc
  2. ()calloc
  3. ()free
  4. ()realloc

لنلقي نظرة على كل دالة فيهم بشكل أوسع

دالة malloc في لغة سي

وهي اختصار لـ “memory allocation” وتستخدم لتخصيص حجرة واحدة كبيرة من الذاكرة ديناميكيًا وبحجم محدد (هذا يعني أن الحجم الأولي لهذه الحجرة في الذاكرة يكون محددًّا لكنه قابل للتغيير فيما بعد ولهذا نقول عنه ديناميكي).

تقوم هذه الدالة بإرجاع مؤشّر من نوع void والذي يمكن تحويله لمؤشر من أي نوع آخر. كما تقوم بتهيئة كل حجرة بقيمة عشوائية افتراضية.

صيغة الدالة:

1
    ptr = (cast-type*) malloc(byte-size)

مثال

1
    ptr = (int*) malloc(100 * sizeof(int));

حيث أن حجم الـ int هو 4 بايت، وبالتالي ستقوم هذه العبارة بتخصيص 400 بايت في الذاكرة. وسيحمل المؤشر ptr عنوان أول بايت في الحجرة المخصصة.

دالة malloc في لغة سي

إذا كانت الذاكرة غير كافية لعملية التخصيص تلك فستفشل العملية وسيتم إرجاع مؤشر NULL.

مثال

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h> 
#include <stdlib.h> 

int main() 
{ 

	// This pointer will hold the 
	// base address of the block created 
	int* ptr; 
	int n, i; 

	// Get the number of elements for the array 
	n = 5; 
	printf("Enter number of elements: %d\n", n); 

	// Dynamically allocate memory using malloc() 
	ptr = (int*)malloc(n * sizeof(int)); 

	// Check if the memory has been successfully 
	// allocated by malloc or not 
	if (ptr == NULL) { 
		printf("Memory not allocated.\n"); 
		exit(0); 
	} 
	else { 

		// Memory has been successfully allocated 
		printf("Memory successfully allocated using malloc.\n"); 

		// Get the elements of the array 
		for (i = 0; i < n; ++i) { 
			ptr[i] = i + 1; 
		} 

		// Print the elements of the array 
		printf("The elements of the array are: "); 
		for (i = 0; i < n; ++i) { 
			printf("%d, ", ptr[i]); 
		} 
	} 

	return 0; 
} 

النتيجة

1
2
3
    Enter number of elements: 5
    Memory successfully allocated using malloc.
    The elements of the array are: 1, 2, 3, 4, 5,

دالة calloc في لغة سي

وهي اختصار لـ “contiguous allocation” أي تخصيص مستمر (متواصل/متجاور) وتستخدم لتخصيص عدد محدد من الحجرات (blocks) في الذاكرة ومن نوع محدد (رقم صحيح أم عشري أم أحرف) ديناميكيًا. وتقوم بتهيئة كل حجرة بالقيمة الافتراضية 0.

صيغة الدالة:

1
    ptr = (cast-type*)calloc(n, element-size);

مثال

1
    ptr = (float*) calloc(25, sizeof(float));

تقوم هذه العبارة بتخصيص مساحة متواصلة أي متجاورة في الذاكرة لـ 25 عنصر وكل منهم بحجم الـ float (أي 4 بايت).

دالة calloc في لغة سي

إذا كانت الذاكرة غير كافية لعملية التخصيص تلك فستفشل العملية وسيتم إرجاع مؤشر NULL.

مثال

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h> 
#include <stdlib.h> 

int main() 
{ 

	// This pointer will hold the 
	// base address of the block created 
	int* ptr; 
	int n, i; 

	// Get the number of elements for the array 
	n = 5; 
	printf("Enter number of elements: %d\n", n); 

	// Dynamically allocate memory using calloc() 
	ptr = (int*)calloc(n, sizeof(int)); 

	// Check if the memory has been successfully 
	// allocated by calloc or not 
	if (ptr == NULL) { 
		printf("Memory not allocated.\n"); 
		exit(0); 
	} 
	else { 

		// Memory has been successfully allocated 
		printf("Memory successfully allocated using calloc.\n"); 

		// Get the elements of the array 
		for (i = 0; i < n; ++i) { 
			ptr[i] = i + 1; 
		} 

		// Print the elements of the array 
		printf("The elements of the array are: "); 
		for (i = 0; i < n; ++i) { 
			printf("%d, ", ptr[i]); 
		} 
	} 

	return 0; 
} 

النتيجة

1
2
3
    Enter number of elements: 5
    Memory successfully allocated using calloc.
    The elements of the array are: 1, 2, 3, 4, 5,

دالة free في لغة سي

وتستخدم لإلغاء تخصيص الذاكرة ديناميكيًا. فعند تخصيص الذاكرة باستخدام malloc و calloc لا يتم إلغاء تخصيصهم تلقائيًا. لذا فإن هذه الدالة free تستخدم في حالة وجود ذاكرة مخصصة ديناميكيا، وتساعد في تخفيض الهدر أو الإسراف في الذاكرة من خلال إلغاء تخصيصها وجعلها حرة للاستخدام.

صيغة الدالة:

1
    free(ptr);

مثال

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <stdio.h> 
#include <stdlib.h> 

int main() 
{ 

	// This pointer will hold the 
	// base address of the block created 
	int *ptr, *ptr1; 
	int n, i; 

	// Get the number of elements for the array 
	n = 5; 
	printf("Enter number of elements: %d\n", n); 

	// Dynamically allocate memory using malloc() 
	ptr = (int*)malloc(n * sizeof(int)); 

	// Dynamically allocate memory using calloc() 
	ptr1 = (int*)calloc(n, sizeof(int)); 

	// Check if the memory has been successfully 
	// allocated by malloc or not 
	if (ptr == NULL || ptr1 == NULL) { 
		printf("Memory not allocated.\n"); 
		exit(0); 
	} 
	else { 

		// Memory has been successfully allocated 
		printf("Memory successfully allocated using malloc.\n"); 

		// Free the memory 
		free(ptr); 
		printf("Malloc Memory successfully freed.\n"); 

		// Memory has been successfully allocated 
		printf("\nMemory successfully allocated using calloc.\n"); 

		// Free the memory 
		free(ptr1); 
		printf("Calloc Memory successfully freed.\n"); 
	} 

	return 0; 
} 

النتيجة

1
2
3
4
5
6
    Enter number of elements: 5
    Memory successfully allocated using malloc.
    Malloc Memory successfully freed.

    Memory successfully allocated using calloc.
    Calloc Memory successfully freed.

أي أنه في هذا المثال قمنا بتخصيص ذاكرة باستخدام malloc ثم تحريرها (إلغاء تخصيصها) باستخدام free ثم تخصيص ذاكرة أخرى باستخدام calloc وتحريرها أيضًا باستخدام free.

دالة realloc في لغة سي

أي “re-allocaion” وتعني إعادة تخصيص وتستخدم لتغيير تخصيص ذاكرة تم تخصيصها من قبل ديناميكيًا. فمثلًا إذا كانت الذاكرة التي تم تخصيصها من قبل باستخدام malloc و calloc لم تعد كافية (احتجنا لإضافة عناصر جديدة وبالتالي توسيعها) فيتم استخدام realloc لإعادة تخصيص الذاكرة ديناميكيًا.

تحافظ إعادة التخصيص هذه على الذاكرة الموجودة أصلا وقيمها وتقوم بتهئية الحجرات الجديدة بقيمة افتراضية عشوائية.

صيغة الدالة:

1
    ptr = realloc(ptr, newSize);

حيث يتم إعادة تخصيص ptr وفق الحجم الجديد “newSize”.

دالة realloc في لغة سي

إذا كانت الذاكرة غير كافية لعملية التخصيص تلك فستفشل العملية وسيتم إرجاع مؤشر NULL.

مثال

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <stdio.h> 
#include <stdlib.h> 

int main() 
{ 

	// This pointer will hold the 
	// base address of the block created 
	int* ptr; 
	int n, i; 

	// Get the number of elements for the array 
	n = 5; 
	printf("Enter number of elements: %d\n", n); 

	// Dynamically allocate memory using calloc() 
	ptr = (int*)calloc(n, sizeof(int)); 

	// Check if the memory has been successfully 
	// allocated by malloc or not 
	if (ptr == NULL) { 
		printf("Memory not allocated.\n"); 
		exit(0); 
	} 
	else { 

		// Memory has been successfully allocated 
		printf("Memory successfully allocated using calloc.\n"); 

		// Get the elements of the array 
		for (i = 0; i < n; ++i) { 
			ptr[i] = i + 1; 
		} 

		// Print the elements of the array 
		printf("The elements of the array are: "); 
		for (i = 0; i < n; ++i) { 
			printf("%d, ", ptr[i]); 
		} 

		// Get the new size for the array 
		n = 10; 
		printf("\n\nEnter the new size of the array: %d\n", n); 

		// Dynamically re-allocate memory using realloc() 
		ptr = realloc(ptr, n * sizeof(int)); 

		// Memory has been successfully allocated 
		printf("Memory successfully re-allocated using realloc.\n"); 

		// Get the new elements of the array 
		for (i = 5; i < n; ++i) { 
			ptr[i] = i + 1; 
		} 

		// Print the elements of the array 
		printf("The elements of the array are: "); 
		for (i = 0; i < n; ++i) { 
			printf("%d, ", ptr[i]); 
		} 

		free(ptr); 
	} 

	return 0; 
} 

النتيجة

1
2
3
4
5
6
7
    Enter number of elements: 5
    Memory successfully allocated using calloc.
    The elements of the array are: 1, 2, 3, 4, 5, 

    Enter the new size of the array: 10
    Memory successfully re-allocated using realloc.
    The elements of the array are: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,

سؤال متعلق بالموضوع

ماهو الفرق بين ()malloc و ()calloc وما هي الدالة في لغة سي التي يجب أن نستخدمها بعد استخدام ()malloc لنصل لنفس وظيفة ()calloc؟

للإجابة على الشق الأول من السؤال يرجى قراءة المقال:الفرق بين calloc و malloc بالأمثلة

أما بالنسبة للدالة التي تحقق نفس وظيفة ()calloc بعد استخدام ()malloc فهي ()memset وذلك لتهيئة العناصر بالقيمة الأولية صفر كما تفعل ()calloc. للمزيد يمكنك قراءة المقال التالي عن memset هنا

يرجى مراسلتي في حال وجود أي استفسار أو إضافة.

هذا المقال مترجم وبتصرّف، المصدر

هذا المنشور تحت ترخيص CC BY 4.0 بواسطة المؤلف.