malloc(3)

الاسم
malloc, free, calloc, realloc, reallocarray - تخصيص وتحرير الذاكرة الديناميكية

المكتبة
مكتبة سي المعيارية (libc، -lc)

موجز
#include <stdlib.h>

void *malloc(size_t size);
void free(void *_Nullable p);
void *calloc(size_t n, size_t size);
void *realloc(void *_Nullable p, size_t size);
void *reallocarray(void *_Nullable p, size_t n, size_t size);

    متطلبات ماكروات اختبار الميزات لـ glibc (انظر feature_test_macros(7)):

reallocarray():
    منذ glibc 2.29:
        _DEFAULT_SOURCE
    glibc 2.28 وما قبله:
        _GNU_SOURCE

الوصف

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

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

  calloc()
تخصص الدالة calloc() ذاكرة لمصفوفة من n عنصر، كل منها بحجم size بايت، وتُعيد مؤشرًا إلى الذاكرة المخصصة. تُضبط الذاكرة على الصفر. إذا كان n أو size يساوي 0، تُعيد calloc() قيمة مؤشر فريدة يمكن تمريرها لاحقًا بنجاح إلى free().

إذا أدى ضرب n و size إلى تجاوز عدد صحيح، تُعيد calloc() خطأ. على النقيض، لا يُكتشف تجاوز عدد صحيح في الاستدعاء التالي لـ malloc()، مما يؤدي إلى تخصيص كتلة ذاكرة بحجم غير صحيح:

    malloc(n * size);

  realloc()
تغير الدالة realloc() حجم كتلة الذاكرة المشار إليها بواسطة p إلى size بايت. تبقى محتويات الذاكرة دون تغيير في النطاق من بداية المنطقة حتى الحد الأدنى للحجمين القديم والجديد. إذا كان الحجم الجديد أكبر من الحجم القديم، فإن الذاكرة المضافة لن تُهيأ.

إذا كان p هو NULL، فإن الاستدعاء يعادل malloc(size)، لجميع قيم size.

إذا كان size يساوي صفرًا، و p ليس NULL، فإن الاستدعاء يعادل free(p) (ولكن انظر "سلوك غير محمول" لمشكلات قابلية النقل).

ما لم يكن p هو NULL، يجب أن يكون قد أُعيد باستدعاء سابق لـ malloc أو دوال ذات صلة. إذا نُقلت المنطقة المشار إليها، تُجرى free(p).

  reallocarray()
تغير الدالة reallocarray() حجم (وربما تنقل) كتلة الذاكرة المشار إليها بواسطة p لتكون كبيرة بما يكفي لمصفوفة من n عنصر، كل منها بحجم size بايت. وهي تعادل الاستدعاء

    realloc(p, n * size);

لكن، على عكس استدعاء realloc() ذلك، تفشل reallocarray() بأمان في حالة حدوث فائض في الضرب. إذا حدث مثل هذا الفائض، تُرجع reallocarray() خطأ.

قيمة الإرجاع
تُرجع دوال malloc() وcalloc() وrealloc() وreallocarray() مؤشرًا إلى الذاكرة المُخصصة، وهي مُحاذاة بشكل مناسب لأي نوع يناسب الحجم المطلوب أو أقل. عند حدوث خطأ، تُرجع هذه الدوال NULL وتُعيِّن errno. تُعد محاولة تخصيص أكثر من PTRDIFF_MAX بايت خطأ، لأن كائنًا بهذا الحجم قد يُسبب فائضًا في طرح المؤشرات لاحقًا.

لا تُرجع دالة free() أي قيمة، وتحافظ على errno.

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

الأخطاء
يمكن أن تفشل دوال calloc() وmalloc() وrealloc() وreallocarray() مع الخطأ التالي:

ENOMEM
    نفاد الذاكرة. ربما بلغ التطبيق حد RLIMIT_AS أو RLIMIT_DATA الموصوف في getrlimit(2). قد يكون سبب آخر هو أن عدد التعيينات التي أنشأتها عملية المُستدعي تجاوز الحد المُحدد بواسطة /proc/sys/vm/max_map_count.

السمات
للاطلاع على شرح للمصطلحات المستخدمة في هذا القسم، انظر attributes(7).
الواجهة | السمة | القيمة
malloc(), free(), calloc(), realloc() | سلامة الخيوط | MT-Safe

المعايير

malloc()
free()
calloc()
realloc()
    C23، ‏POSIX.1-2024.

reallocarray()
    POSIX.1-2024.

  realloc(p, 0)
لا يتوافق سلوك realloc(p, ) في glibc مع أي من C99 أو C11 أو POSIX.1-2001 أو POSIX.1-2004 أو POSIX.1-2008 أو POSIX.1-2013 أو POSIX.1-2017 أو POSIX.1-2024. غُيّرت مواصفات C17 لجعلها متوافقة، لكن تلك المواصفات جعلت من المستحيل كتابة كود يُحدد بشكل موثوق ما إذا كان المؤشر المُدخل قد حُرر بعد realloc(p, 0)، وغيّرتها C23 مرة أخرى لجعل هذا سلوكًا غير مُحدد، معترفةً بأن مواصفات C17 كانت واسعة بما يكفي، بحيث لم يكن السلوك غير المُحدد أسوأ من ذلك.

تعاني reallocarray() من نفس المشكلات في glibc.

تتوافق musl libc و BSDs مع جميع إصدارات ISO C و POSIX.1.

يوفر gnulib الوحدة realloc-posix، التي تُوفر غلافات realloc() وreallocarray() تتوافق مع جميع إصدارات ISO C و POSIX.1.

هناك اقتراح لتوحيد سلوك BSD: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3621.txt.

التاريخ

malloc()
free()
calloc()
realloc()
    POSIX.1-2001، C89.

reallocarray()
    glibc 2.26. OpenBSD 5.6, FreeBSD 11.0.

رفضت malloc() والدوال ذات الصلة الأحجام الأكبر من PTRDIFF_MAX بدءًا من glibc 2.30.

الدالة free() تحافظ على errno ابتداءً من glibc 2.33.

  realloc(p, 0)
كانت مواصفات C89 غامضة بخصوص realloc(p, 0). صحّح C99 هذا جزئيًا.

كان التنفيذ الأصلي في glibc متوافقًا مع C99. لكن، ومن المفارقات، أن محاولة الامتثال لـ C99 قبل إصدار المعيار، غيّرت glibc سلوكها في glibc 2.1.1 إلى شيء لم ينتهِ به المطاف متوافقًا مع مواصفات C99 النهائية (لكن هذا محل جدل، إذ تبدو صياغة المعيار متناقضة مع ذاتها).

ملاحظات
مبدئيًا، يتبع لينكس استراتيجية تفاؤلية لتخصيص الذاكرة. هذا يعني أنه عندما تُرجع malloc() قيمة غير NULL، فلا يوجد ضمان بأن الذاكرة متاحة فعلًا. في حال اتضح أن النظام قد نفدت ذاكرته، فسيُقتل عملية أو أكثر بواسطة قاتل OOM. لمزيد من المعلومات، انظر وصف /proc/sys/vm/overcommit_memory و /proc/sys/vm/oom_adj في proc(5)، وملف مصدر نواة لينكس Documentation/vm/overcommit-accounting.rst.

عادةً، تخصص malloc() الذاكرة من الكومة، وتضبط حجم الكومة حسب الحاجة، باستخدام sbrk(2). عند تخصيص كتل ذاكرة أكبر من MMAP_THRESHOLD بايت، ينفذ تنفيذ glibc لـ malloc() تخصيص الذاكرة كتعيين مجهول خاص باستخدام mmap(2). قيمة MMAP_THRESHOLD هي 128 كيلوبايت مبدئيًا، لكنها قابلة للضبط باستخدام mallopt(3). قبل لينكس 4.7، لم تتأثر التخصيصات المنفذة باستخدام mmap(2) بحد المورد RLIMIT_DATA؛ منذ لينكس 4.7، يُطبّق هذا الحد أيضًا على التخصيصات المنفذة باستخدام mmap(2).

لتجنب الفساد في التطبيقات متعددة الخيوط، تُستخدم كائنات المزامنة (mutexes) داخليًا لحماية هياكل بيانات إدارة الذاكرة التي توظفها هذه الدوال. في تطبيق متعدد الخيوط حيث تخصص الخيوط الذاكرة وتحررها في وقت واحد، قد يحدث تنافس على كائنات المزامنة هذه. للتعامل مع تخصيص الذاكرة بشكل قابل للتوسع في التطبيقات متعددة الخيوط، تنشئ glibc ساحات تخصيص ذاكرة إضافية إذا اكتُشف تنافس على كائن المزامنة. كل ساحة هي منطقة كبيرة من الذاكرة تخصصها النظام داخليًا (باستخدام brk(2) أو mmap(2))، وتُدار بكائنات المزامنة الخاصة بها.

إذا استخدم برنامجك مخصص ذاكرة خاصًا، فيجب أن يفعل ذلك عن طريق استبدال malloc() و free() و calloc() و realloc(). يجب أن تنفذ دوال الاستبدال السلوكيات الموثقة لـ glibc، بما في ذلك معالجة errno، وتخصيصات الحجم صفر، والتحقق من الفائض؛ وإلا، فقد تتعطل أو تعمل بشكل غير صحيح روتينات مكتبة أخرى. على سبيل المثال، إذا لم تحافظ دالة free() البديلة على errno، فقد تفشل روتينات مكتبة تبدو غير ذات صلة دون سبب صحيح في errno. قد تحتاج مخصصات الذاكرة الخاصة أيضًا إلى استبدال دوال glibc أخرى؛ انظر "Replacing malloc" في دليل glibc للتفاصيل.

ترتبط الأعطال في مخصصات الذاكرة دائمًا تقريبًا بفساد الكومة، مثل تجاوز سعة قطعة مخصصة أو تحرير نفس المؤشر مرتين.

تنفيذ malloc() قابل للضبط عبر متغيرات البيئة؛ انظر mallopt(3) للتفاصيل.

  سلوك غير محمول
سلوك هذه الدوال عندما يكون الحجم المطلوب صفرًا خاص بـ glibc؛ قد تُرجع تطبيقات أخرى NULL دون تعيين errno، ويجب على برامج POSIX المحمولة تحمل مثل هذا السلوك. انظر realloc(3p).

يتطلب POSIX من مخصصات الذاكرة تعيين errno عند الفشل. لكن، لا يتطلب معيار C هذا، ولا ينبغي للتطبيقات المحمولة إلى منصات غير POSIX افتراض ذلك.

لا ينبغي للبرامج المحمولة استخدام مخصصات ذاكرة خاصة، إذ لا يسمح POSIX ومعيار C باستبدال malloc() و free() و calloc() و realloc().

العلل
سيتوقع المبرمجون طبيعيًا بالاستقراء أن realloc(p, size) متسق مع free(p) و malloc(size)، حيث أن هذا هو السلوك في الحالة العامة. هذا غير مطلوب صراحةً بواسطة POSIX.1-2024 أو C11، لكن جميع التطبيقات المطابقة متسقة مع ذلك.

تطبيق glibc للدالة realloc() غير متسق مع ذلك، وكنتيجة لذلك، من الخطر استدعاء realloc(p, 0) في glibc.

حل بديل تافه لـ glibc هو استدعاؤها كـ realloc(p, size?size:1).

الحل البديل للدالة reallocarray() في glibc —التي تشارك نفس الخلل— سيكون reallocarray(p, n?n:1, size?size:1).

أمثلة
    #include <err.h>
    #include <stddef.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    #define MALLOCARRAY(n, type)  ((type *) my_mallocarray(n, sizeof(type)))
    #define MALLOC(type)          MALLOCARRAY(1, type)

    static inline void *my_mallocarray(size_t n, size_t size);

    int
    main(void)
    {
        char  *p;

        p = MALLOCARRAY(32, char);
        if (p == NULL)
            err(EXIT_FAILURE, "malloc");

        strlcpy(p, "foo", 32);
        puts(p);
    }

    static inline void *
    my_mallocarray(size_t n, size_t size)
    {
        return reallocarray(NULL, n, size);
    }

انظر أيضًا
valgrind(1), brk(2), mmap(2), alloca(3), malloc_get_state(3), malloc_info(3), malloc_trim(3), malloc_usable_size(3), mallopt(3), mcheck(3), mtrace(3), posix_memalign(3)

لتفاصيل تنفيذ مكتبة سي لجنو، انظر https:/sourceware.org/glibc/wiki/MallocInternals.

ترجمة
تُرجمت هذه الصفحة من الدليل بواسطة زايد السعيدي <zayed.alsaidi@gmail.com>

هذه الترجمة هي وثيقة مجانية؛ راجع رخصة جنو العامة الإصدار 3 (https://www.gnu.org/licenses/gpl-3.0.html) أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات.

إذا وجدت أي أخطاء في ترجمة صفحة الدليل هذه، يرجى إرسال بريد إلكتروني إلى قائمة بريد المترجمين: kde-l10n-ar@kde.org.