دالة malloc() في مكتبة C مع EXAMPLE

⚡ ملخص ذكي

تقوم الدالة malloc() في لغة C بحجز كتلة من الذاكرة على الكومة في وقت التشغيل وتعيد مؤشرًا فارغًا إلى البايت الأول، مما يسمح للبرامج بطلب مساحة تخزين غير معروفة الحجم حتى يتم تنفيذ الكود فعليًا.

  • 🧠 تخصيص وقت التشغيل: تقوم الدالة malloc() بحجز الذاكرة أثناء التنفيذ عندما لا يكون الحجم المطلوب معروفًا في وقت الترجمة.
  • 📦 بناء الجملة: يستقبل مؤشر cast_type دالة malloc(byte_size)، والتي يتم تحديد حجمها عادةً باستخدام sizeof من أجل قابلية النقل.
  • ⚠️ التحقق من القيمة الفارغة: يؤدي الطلب الفاشل إلى إرجاع مؤشر فارغ، لذا تحقق من صحة النتيجة قبل إلغاء مرجعيتها.
  • ➕ العمليات الحسابية باستخدام المؤشرات: الكتلة متجاورة، لذا استخدم + بدلاً من ++ للوصول إلى عناصر المصفوفة.
  • 🧹 حرر الذاكرة: قم بتحرير كل كتلة باستخدام الدالة free() لمنع تسرب الذاكرة والمؤشرات المعلقة.
  • 🤖 مساعدة الذكاء الاصطناعي: يقوم مساعدو الذكاء الاصطناعي و GitHub Copilot بصياغة استدعاءات malloc() والإبلاغ عن عبارات free() المفقودة.

دالة malloc() في لغة C

ما هو malloc في C؟

الدالة malloc() تعني تخصيص الذاكرة. إنها وظيفة تُستخدم لتخصيص كتلة من الذاكرة ديناميكيًا. يقوم بحجز مساحة ذاكرة بالحجم المحدد ويعيد المؤشر الفارغ الذي يشير إلى موقع الذاكرة. المؤشر الذي تم إرجاعه عادة ما يكون من النوع الفارغ. هذا يعني أنه يمكننا تعيين وظيفة malloc لأي مؤشر.

ما هو malloc في C

بناء الجملة

ptr = (cast_type *) malloc (byte_size);

هنا،

  • ptr هو مؤشر cast_type.
  • تقوم الدالة malloc بإرجاع مؤشر إلى الذاكرة المخصصة لـ byte_size.
Example: ptr = (int *) malloc (50)

عند تنفيذ هذا البيان بنجاح، يتم حجز مساحة ذاكرة قدرها 50 بايت. يتم تعيين عنوان البايت الأول من المساحة المحجوزة للمؤشر ptr من النوع int.

فكر في مثال آخر لتطبيق malloc:

#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 مع نوع بيانات الحرف بالإضافة إلى أنواع البيانات المعقدة مثل الهياكل.

لماذا نستخدم دالة malloc() في لغة C؟

تُحدد المصفوفات الثابتة والمتغيرات العادية حجمها عند تجميع البرنامج، مما يُهدر مساحةً عندما يكون حجم البيانات المطلوبة أصغر، ويتسبب في فشل البرنامج تمامًا عندما يكون أكبر. تحل دالة malloc() هذه المشكلة عن طريق طلب الذاكرة أثناء التشغيل، بحيث يُمكن للبرنامج تحديد حجم المخزن المؤقت بدقة ليتناسب مع كمية البيانات التي يتلقاها. تُعد هذه المرونة أثناء التشغيل جوهر... تخصيص الذاكرة الديناميكية في C.

يُعد التخصيص الديناميكي باستخدام malloc() الخيار الأمثل في العديد من الحالات الشائعة:

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

لأن الكتلة المحجوزة يتم إرجاعها كمؤشر، يمكن تمريرها بين الوظائف بتكلفة منخفضة دون نسخ البيانات الأساسية.

كيفية تحرير الذاكرة المخصصة بواسطة malloc()

تُخزَّن الذاكرة المحجوزة بواسطة دالة malloc() في الذاكرة الديناميكية (heap) ولا تُحرَّر تلقائيًا. عندما يستمر البرنامج في تخصيص الذاكرة دون تحريرها، فإنه يستهلك تدريجيًا كل الذاكرة المتاحة، وهو عيب يُعرف بتسرب الذاكرة. تُعيد دالة free() كتلة مُكتملة إلى النظام حتى يُمكن إعادة استخدام المساحة.

تأخذ الدالة free() وسيطًا واحدًا، وهو المؤشر الذي أعادته الدالة malloc()، وتقوم بتحرير الكتلة بأكملها دفعة واحدة:

int *ptr;
ptr = malloc(10 * sizeof(int));
if (ptr != NULL) {
    /* use the allocated memory here */
    free(ptr);   /* release the block */
    ptr = NULL;  /* avoid a dangling pointer */
}

اتبع هذه القواعد لإدارة الذاكرة الديناميكية بأمان:

  • قم باستدعاء الدالة free() مرة واحدة فقط لكل عملية تخصيص ناجحة للكتلة malloc()؛ فتحرير نفس الكتلة مرتين يؤدي إلى سلوك غير محدد.
  • قم بتعيين المؤشر إلى NULL بعد تحريره حتى لا يمكن إعادة استخدامه كمؤشر معلق.
  • لا تقم أبدًا بتحرير مؤشر لم تقم دالة malloc() بإرجاعه، بما في ذلك المؤشر الذي تم إزاحته بواسطة العمليات الحسابية للمؤشرات.
  • قم بمطابقة كل عملية تخصيص مع عملية تحرير مطابقة قبل أن يخرج المؤشر عن النطاق.

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

malloc() مقابل calloc(): الاختلافات الرئيسية

malloc() و calloc () كلاهما يحجزان مساحة في الذاكرة الديناميكية، لكنهما يختلفان في وسائطهما وكيفية تعاملهما مع الكتلة الجديدة. تأخذ الدالة malloc() وسيطًا واحدًا، وهو العدد الإجمالي للبايتات، وتترك محتويات الكتلة غير مهيأة، لذا تحتوي الكتلة على قيم عشوائية غير متوقعة حتى يكتب البرنامج إليها. أما الدالة calloc() فتأخذ وسيطين، وهما عدد العناصر وحجم كل عنصر، وتقوم بمسح كل بايت إلى الصفر قبل إعادة المؤشر.

تتلخص الاختلافات العملية في ثلاث نقاط:

  • التهيئة: تقوم الدالة calloc() بتصفير الذاكرة، بينما لا تقوم الدالة malloc() بذلك، مما يجعل الدالة malloc() أسرع قليلاً عندما يتم استبدال القيم على أي حال.
  • الحجج: أنت تكتب malloc(n * size) ولكن calloc(n, size) لحجز نفس المصفوفة.
  • حالة الاستخدام: اختر calloc() عندما يكون من المهم وجود كتلة نظيفة ومُصفّرة، واستخدم malloc() عندما تكون السرعة هي الأفضل ويتم التعامل مع التهيئة يدويًا.

تم تعريف كلا الدالتين في stdlib.h، وكلاهما يعيد مؤشرًا فارغًا أو NULL عند الفشل، وكلاهما يعيد الذاكرة باستخدام نفس الدالة free().

الأسئلة الشائعة

في لغة C، يُعدّ التحويل اختياريًا وغالبًا ما يُنصح بتجنبه، لأن void* يُحوّل تلقائيًا إلى أي مؤشر كائن، وقد يُخفي التحويل الصريح وجود ملف stdlib.h مفقود. C++ومع ذلك، فإن طاقم التمثيل مطلوب.

يشير إرجاع قيمة فارغة (NULL) إلى فشل الطلب، وعادةً ما يكون السبب هو عدم كفاية الذاكرة. تحقق دائمًا من المؤشر قبل استخدامه، لأن محاولة الوصول إلى قيمة فارغة (NULL) تؤدي إلى سلوك غير متوقع، وعادةً ما تتسبب في تعطل البرنامج.

تقوم الدالة realloc() بتغيير حجم كتلة تم حجزها مسبقًا بواسطة الدالة malloc()، وتحتفظ بها.ping يتم تحديث البيانات الموجودة حتى الوصول إلى الحجم الأصغر. في حال فشل العملية، تُرجع قيمة NULL وتترك الكتلة الأصلية سليمة، لذا يُنصح بتعيين النتيجة إلى مؤشر مؤقت أولاً.

يحدث تسرب الذاكرة عندما لا يتم تحرير الذاكرة المخصصة (heap) من خلال دالة malloc() باستخدام دالة free(). يستمر البرنامج في حجز مساحة لم يعد يستخدمها، مما قد يؤدي تدريجياً إلى استنزاف الذاكرة المتاحة له.

تحجز الدالة malloc() الذاكرة في الكومة، وليس في المكدس. تبقى كتل الكومة صالحة حتى تستدعي الدالة free()، لذا يمكنها البقاء بعد انتهاء الدالة التي أنشأتها، على عكس متغيرات المكدس التلقائية التي تختفي عند العودة.

malloc() هي دالة مكتبة تُرجع void* وتحتاج إلى sizeof لحساب البايتات. C++ يُعدّ new عاملًا يُعيد مؤشرًا مُحدد النوع ويستدعي الدوال البانية. استخدم malloc() مع free()، وnew مع delete.

نعم. تقوم برامج الذكاء الاصطناعي المساعدة في البرمجة بصياغة استدعاءات malloc()، وإضافة عمليات التحقق من القيم الفارغة (NULL)، والإشارة إلى استدعاءات free() المفقودة أو تسريبات الذاكرة، كما يمكنها شرح العمليات الحسابية للمؤشرات. لذا، احرص دائمًا على تجميع الكود المُولّد واختباره قبل اعتماده.

مساعد الطيار جيثب يقترح هذا التعليق استخدام الدوال malloc() و sizeof() و free() المطابقة من خلال وصف الغرض من استخدامك. Revراجع كل اقتراح بخصوص عمليات التحقق المفقودة من القيم الفارغة (NULL) والتسريبات قبل التجميع.

تلخيص هذه التدوينة بـ: