سؤال

هذا السؤال لديه بالفعل إجابة هنا:

ماذا فعلت malloc(0) عائدات؟ هل سيكون الجواب نفسه realloc(malloc(0),0) ?

#include<stdio.h>
#include<malloc.h>
int main()
{
        printf("%p\n", malloc(0));
        printf("%p\n", realloc(malloc(0), 0));
        return 0;
}

الإخراج من Linux GCC:

manav@manav-workstation:~$ gcc -Wall mal.c
manav@manav-workstation:~$ ./a.out
0x9363008
(nil)
manav@manav-workstation:~$

يستمر الإخراج في التغيير في كل مرة malloc(0). هل هذه إجابة قياسية؟ ولماذا يهتم أي شخص بالحصول على مثل هذا المؤشر ، بخلاف البحث الأكاديمي؟

تعديل:

لو malloc(0) إرجاع المؤشر الوهمي ، ثم كيف يعمل التالية:

int main()
{
    void *ptr = malloc(0);
    printf("%p\n", realloc(ptr, 1024));
    return 0;
}

تعديل:

الرمز التالي يخرج "ممكن" لكل تكرار. لماذا لا تفشل؟

#include<stdio.h>
#include<malloc.h>
int main()
{

        int i;
        void *ptr;
        printf("Testing using BRUTE FORCE\n");
        for (i=0; i<65000; i++)
        {
                ptr = malloc(0);
                if (ptr == realloc(ptr, 1024))
                        printf("Iteration %d: possible\n", i);
                else
                {
                        printf("Failed for iteration %d\n", i);
                        break;
                }
        }
        return 0;
}
هل كانت مفيدة؟

المحلول

أجاب الآخرون كيف malloc(0) يعمل. سأجيب على أحد الأسئلة التي طرحتها لم يتم الإجابة عليها بعد (على ما أظن). السؤال هو عن realloc(malloc(0), 0):

ماذا فعلت malloc(0) إرجاع؟ هل سيكون الجواب نفسه realloc(malloc(0),0)?

المعيار يقول هذا عن realloc(ptr, size):

  • لو ptr هو NULL, ، يتصرف مثل malloc(size),
  • غير ذلك (ptr ليس NULL) ، يقوم بتعامل مؤشر الكائن القديم إليه بواسطة ptr ويعيد مؤشر إلى عازلة مخصصة جديدة. لكن اذا size هو 0 ، C89 يقول أن التأثير يعادل free(ptr). ومن المثير للاهتمام ، لا يمكنني العثور على هذا البيان في مسودة C99 (N1256 أو N1336). في C89 ، ستكون القيمة الوحيدة المعقولة للعودة في هذه الحالة NULL.

لذلك ، هناك حالتان:

  • malloc(0) عائدات NULL على التنفيذ. ثم الخاص بك realloc() المكالمة تعادل realloc(NULL, 0). هذا يعادل malloc(0) من فوق (وهذا هو NULL في هذه الحالة).
  • malloc(0) يعود غيرNULL. ثم ، المكالمة تعادل free(malloc(0)). في هذه الحالة، malloc(0) و realloc(malloc(0), 0) نكون ليس ما يعادل.

لاحظ أن هناك حالة مثيرة للاهتمام هنا: في الحالة الثانية ، متى malloc(0) يعود غيرNULL عند النجاح ، قد لا يزال يعود NULL للإشارة إلى الفشل. سيؤدي هذا إلى مكالمة مثل: realloc(NULL, 0), ، والتي ستكون مكافئة malloc(0), ، والتي قد تعود أو لا تعود NULL.

لست متأكدًا مما إذا كان الإغفال في C99 هو إشراف أو إذا كان ذلك يعني أنه في C99 ، realloc(ptr, 0) لغيرNULL ptr لا يعادل free(ptr). لقد جربت هذا مع gcc -std=c99, وما سبق يعادل free(ptr).

تعديل: أعتقد أنني أفهم ما هو ارتباك الخاص بك:

دعونا نلقي نظرة على مقتطف من رمز المثال الخاص بك:

ptr = malloc(0);
if (ptr == realloc(ptr, 1024))

ما سبق ليس هو نفسه malloc(0) == realloc(malloc(0), 1024). في الثانية ، malloc() يتم إجراء المكالمة مرتين ، بينما في البداية ، تمر مؤشرًا مخصصًا مسبقًا إليه realloc().

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

الآن السؤال الذي تطرحه: malloc(0) == realloc(malloc(0), 1024). في هذه الحالة ، دعنا نفترض أن كلاهما malloc(0) على LHS و RHS يعود غيرNULL. ثم ، يتم ضمان أن تكون مختلفة. أيضا ، قيمة الإرجاع من malloc() على LHS لم يكن free()D حتى الآن ، لذلك أي شخص آخر malloc(), calloc(), ، أو realloc() لا يجوز لإرجاع هذه القيمة. هذا يعني أنه إذا كتبت حالتك على النحو التالي:

if (malloc(0) == realloc(malloc(0), 1024)
    puts("possible");

لن ترى possible على الإخراج (ما لم يكن كلاهما malloc() و realloc() فشل والعودة NULL).

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    void *p1;
    void *p2;

    p1 = malloc(0);
    p2 = realloc(p1, 1024);
    if (p1 == p2)
        puts("possible, OK");

    /* Ignore the memory leaks */
    if (malloc(0) == realloc(malloc(0), 1024))
        puts("shouldn't happen, something is wrong");
    return 0;
}

في OS X ، لم يخرج الكود الخاص بي أي شيء عندما قمت بتشغيله. على Linux ، يطبع possible, OK.

نصائح أخرى

malloc(0) هو تم تعريف التنفيذ بقدر ما يتعلق الأمر C99.

من عند C99 القسم 7.20.3

ترتيب وتواصل التخزين المخصص للمكالمات المتتالية إلى وظائف Calloc و Malloc و RealLoc غير محددة. تم إرجاع المؤشر الذي تم إرجاعه في حالة نجاح التخصيص بشكل مناسب بحيث يمكن تعيينه إلى مؤشر إلى أي نوع من الكائنات ثم يستخدم للوصول إلى مثل هذا الكائن أو مجموعة من هذه الكائنات في المساحة المخصصة (حتى يتم تخصيص المساحة بشكل صريح) . يمتد عمر كائن مخصص من التخصيص حتى التخصيص. يجب أن تسفر كل تخصيص من هذا القبيل مؤشر إلى كائن تفكيك من أي كائن آخر. عاد المؤشر إلى نقطة البداية (أدنى عنوان بايت) للمساحة المخصصة. إذا كان لا يمكن تخصيص المساحة ، يتم إرجاع مؤشر فارغ. إذا كان حجم المساحة المطلوبة صفرًا ، فسيتم تنفيذ السلوك: إما أن يتم إرجاع مؤشر فارغ ، أو أن السلوك كما لو كان الحجم بعض القيمة غير الصفر ، إلا أنه لا ينبغي استخدام المؤشر الذي تم إرجاعه للوصول إلى كائن.

في C89 ، يعتمد Malloc (0) على التنفيذ - لا أعرف ما إذا كان C99 قد حدد هذا أم لا. في C ++ ، باستخدام:

char * p = new char[0];

محدد بشكل جيد - تحصل على مؤشر صالح وغير فائق. بالطبع ، لا يمكنك استخدام المؤشر للوصول إلى ما يشير إليه دون استدعاء سلوك غير محدد.

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

C99 المعيار

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

ال Comp.lang.c الأسئلة الشائعة لديها ما يلي ليقول:

يقول معيار ANSI/ISO أنه قد يفعل أيضًا ؛ السلوك محدد التنفيذ (انظر السؤال 11.33). يجب أن تحرص الرمز المحمول على عدم الاتصال بـ Malloc (0) ، أو أن تكون مستعدًا لإمكانية عودة فارغة.

لذلك ، من الأفضل تجنب استخدام malloc(0).

انظر C99 ، القسم 7.20.3:

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

هذا صالح لجميع وظائف التخصيص الثلاثة (أي calloc(), malloc() و realloc()).

نقطة واحدة لا أحد يهتم بالتحدث بعد ، في برنامجك الأول هو ذلك realloc مع الطول 0 هو نفس الشيء free.

من صفحة Solaris Man:

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

إذا كان المرء لا يعلم أنه يمكن أن يكون مصدرًا للمفاجأة السيئة (حدث لي).

أعتقد أن ذلك يعتمد. راجعت مصادر Visual Studio 2005 ورأيت هذا في وظيفة _Heap_Alloc:

if (size == 0)
    size = 1;

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

إذا قام Malloc (0) بإرجاع المؤشر الوهمي ، فكيف يعمل التالية:

void *ptr = malloc(0);

printf("%p\n", realloc(ptr, 1024));

لا أعرف ماذا تقصد بـ "مؤشر وهمية". لو malloc(0) يعود غير خارق ، ثم ptr هو مؤشر صالح إلى كتلة ذاكرة من الصفر. ال malloc ينقذ التنفيذ هذه المعلومات بطريقة خاصة بالتنفيذ. realloc يعرف الطريقة (الخاصة بالتنفيذ) لمعرفة ذلك ptr يشير إلى كتلة ذاكرة من الصفر.

(كيف malloc/realloc/free هل هذا هو التنفيذ الخاص. أحد الاحتمالات هو تخصيص 4 بايت أكثر من المطلوب وتخزين الحجم قبل كتلة الذاكرة مباشرة. في هذه الحالة، ((int *)ptr)[-1] يعطي حجم كتلة الذاكرة ، وهو 0. يجب ألا تفعل ذلك أبدًا من الكود الخاص بك ، إنه فقط للاستخدام بواسطة realloc و free).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top