A Crash Course in C - Part 6

Dynamic Memory Allocation.

الذاكرة الديناميكية Dynamic Memory

في لغة C يتم التعامل مع البيانات عن طريق اربع مناطق في الذاكرة. منطقة البيانات الثابتة constant data area ، منطقة البيانات ذات المدي الثابت static-extent data area ، المكدس stack و الـ heap.

في منطقة البيانات الثابتة يتم تخزين الثوابت النصية و البيانات الآخري المعروفة للمترجم Compiler اثناء عملية التجميع compilation، هذه المنطقة من الذاكرة تمتلك الأذن فقط بالقراءة read-only permission ولا يمكن لأي جزء آخر من البرنامج التعديل عليها اثناء تشغيله.

في منطقة البيانات ذات المدي الثابت يتم تخزين المتغيرات التي تم تعريفها بستخدام extern او static، مما يترتب عليه وجودها في الذاكرة طوال فترة تشغيل البرنامج، هذه المنطقة من الذاكرة تمتلك الأذونات بالقراءة و التعديل عليها read-writable permissions. و يتم تعيين منطقة البيانات الثابتة و منطقة البيانات ذات المدي الثابت منذ بداية البرنامج حتي إنتهاء تشغيله و يتم تحديد و ادارة عملهم من قبل المترجم اثناء عملية التجميع.

بالنسبة لمنطقة الذاكرة المعروفة بالمكدس Stack فتستخدم لتخزين المتغيرات المحلية (التي تمتلك مدي تلقائي)، حيث يتم التعريف عن المتغير و تعيين قيمته داخل الدالة و بعد انتهاء عمل الدالة يتم تفريغ تلك المساحة من الذاكرة بداية من أخر قيمة تم اضافتها له حتي اول قيمة بسبب كون المكدس من النوع Last in first out LIFO . المكدس هو الآخر يتم ادارته بشكل كامل من قبل المترجم.

اما عن الـ Heap فيتم استخدامه في تخصيص الذاكرة بشكل ديناميكي و يتم ادارتها من قبل المبرمج بدلا من المترجم. و يتم تعيين و تفريغ البيانات من الـ Heap عن طريق استخدام مجموعة من دوال المكتبة القياسية، و يترك للمبرمج كامل السيطرة علي عمر تلك البيانات داخل الذاكرة. وبسبب ان ادارة الـ Heap تقع علي المبرمج ولا يتم مراجعة صحة ادارتها من قبل المترجم ، تزيد احتمالية وجود اخطاء الذاكرة الديناميكية dynamic memory errors.

 

دوال تخصيص الذاكرة القياسية Standard memory allocation functions

توفر المكتبة القياسية دوال لتخصيص و وتفريع الذاكرة بشكل ديناميكي هم ()malloc و ()free و ()calloc و ()realloc و هم جزء من الملف الرأسي stdlib.h.

تمتلك ()malloc الـ function prototype التالي:

حيث يمثل الـ size حجم الـ bytes المطلوب تخصيصها في الذاكرة، و القيمة العائدة من تلك الدالة هي مؤشر للمساحة التي تم تخصيصها. لاحظ ان المؤشر العائد من نوع * void مما يشير الي امكانية تعيين قيمة في تلك المساحة المخصصة من اي نوع بيانات. مثال لإنشاء مصفوفة من عشرة ارقام يمكننا كتابة السطر التالي:

لاحظ استخدام sizeof مع نوع البيانات المطلوب int مضروب في عدد عناصر المصفوفة 10 هذا بسبب ان المساحة المطلوب تخصيصها في الذاكر ستخزن قيمة من نوع int. المثال السابق يعمل جيدا ولكن غالبا ما يفضل استخدام الـ casts ليكون الملف المصدري اوضح اثناء قرائته.

المثال التالي يوضح استخدام ()malloc لإنشاء مصفوفة من الأرقام الصحيحة تحتوي علي خمس عنصار:

malloc

تتشابه كثيرا ()calloc مع ()malloc لكن تختلف في القيمة المرجعة فبينما تعيد ()malloc مؤشر لمساحة من ذاكرة غير مهيء uninitialised memory block يحتوي علي قيم عشوائية من البيانات، تقوم ()calloc بتهئية initialise المساحة المطلوبة من الذاكرة بإعطائها القيمة 0. اما بالنسبة للـ function prototype فيختلف قليلا عن ()malloc لانه يأخذ وسيطين علي الشكل التالي:

حيث يحدد اول وسيط كم المساحة المطلوب تخصيصها من نوع بيانات معين. و يحدد ثاني وسيط حجم نوع البيانات لتلك المساحة. نفس المثال السابق بستخدام ()calloc:

calloc

اما بالنسبة لإلغاء تخصيص مساحة الذاكرة السابق تخصيصها بستخدام ()malloc او ()calloc و إرجاعها الي الـ heap نقوم بستخدام ()free. تمتلك الدالة ()free الـ function prototype التالي:

المثال التالي يوضح استخدام ()free لإلغاء تخصيص مصفوفتين بحجم خمسة عنصار من نوع int تم إنشاءهم بستخدام ()calloc و ()malloc:

 

و تستخدم ()realloc لإعادة تخصيص مساحة من الذاكرة، بمعني آخر تُئستخدم لتغيير حجم الذاكرة التي تم تخصيصها بشكل ديناميكي بواسطة ()malloc او ()calloc او حتي ()realloc نفسها. و يأخذ الـ function prototype الخاص بها الشكل التالي:

حيث يمثل المؤشر p المؤشر لعنوان الذاكرة السابق تخصيصها و الـ size المساحة الجديدة لذلك العنوان. اما بالنسبة للقيمة المرجعة فتكون مؤشر لنفس عنوان الذاكرة. في حالة مساواة الـ size لـ 0 ستعمل ()realloc كـ ()free و سيتم إلغاء تخصيص المساحة. استخدام ()realloc لا يغير محتوي الذاكرة المخصصة، في حالة كان حجم المساحة في الوسيط الثاني اقل من حجم المساحة التي يشير اليها المؤشر في الوسيط الأول سيتم التخلص من البيانات في المساحة الزائدة و ستترك البقية كما هي. و في حالة كانت المساحة اكبر من المساحة الحالية فسيتم زيادة مساحة الذاكرة المخصصة و ستترك القيم الموجودة بدون تعديل و المساحة الجديدة ستحتوي علي قيم عشوائية. مثال علي استخدام ()realloc لزيادة حجم مصفوفة من 5 عناصر لـ 10 عناصر:

realloc

 

مصفوفة قابلة للإتساع An Expandable Array

في لغة C تمتلك المصفوفات حجم عناصر ثابت. يتم تحديده اثناء عملية الترجمة، نفس الأمر ينطبق علي المصفوفات التي يتم تحديد عدد عناصرها بشكل ديناميكي (المصفوفات التي لا تمتلك رقم بين [] عند التعريف عنها). في بعض الحالات يكون من الأفضل امتلاك البرنامج لمصفوفة يمكن زيادة عدد عناصرها بشكل ديناميكي، هنا تظهر فائدة ما يمكن انه يوفره الـ heap. المثال التالي يقبل من المستخدم عدد غير محدد من الأرقام التي لا تساوي 0 و يقوم بتخزينهم بشكل ديناميكي داخل مصفوفة، في حالة ادخال الرقم 0 سيخرج البرنامج من حلقة while و يقوم بختيار عنصر من عناصر المصفوفة بشكل عشوائي و طباعتها.

dynamicarr