هل يجب أن أقوم بفرض التحقق من releloc ما إذا كان حجم الكتلة الجديد أصغر من الأولي؟

StackOverflow https://stackoverflow.com/questions/2541215

  •  23-09-2019
  •  | 
  •  

سؤال

هل يمكن أن تفشل Realloc في هذه الحالة؟

int *a = NULL;

a = calloc(100, sizeof(*a));
printf("1.ptr: %d\n", a);

a = realloc(a, 50 * sizeof(*a));
printf("2.ptr: %d\n", a);

if(a == NULL){
    printf("Is it possible?\n");
}

return (0);

}

الإخراج في حالتي هو:

1.ptr: 4072560
2.ptr: 4072560

لذلك "أ" يشير إلى نفس الامتصاص. فهل يجب أن أفرض شيكًا Realloc؟

تحرير في وقت لاحق:

  • باستخدام برنامج التحويل البرمجي Mingw تحت Windows XP.
  • هل السلوك مشابه مع GCC على Linux؟

تحرير في وقت لاحق 2: هل من المقبول التحقق من هذه الطريقة؟

int *a = NULL, *b = NULL;

a = calloc(100, sizeof(*a));
b = realloc(a, 50 * sizeof(*a));

if(b == NULL){
    return a;
}
a = b;
return a;
هل كانت مفيدة؟

المحلول

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

السلوك الحالي لإعادة استخدام العنوان نفسه هو تفاصيل التنفيذ التي لا ينبغي الاعتماد عليها. القيام بذلك هو فتح نفسك فقط للأخطاء عندما تقوم المكتبة بتبديل تنفيذها أو تنتقل إلى منصة جديدة.

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

void* xrealloc(void* ptr, size_t size) {
  ptr = realloc(ptr, size);
  if ( !ptr ) {
    exit(EXIT_FAILURE);
  }
  return ptr;
}

نصائح أخرى

سيكون من المستغرب إذا realloc فشل عند اجتياز حجم أصغر من التخصيص الأصلي ، ولكن لا شيء في معيار C (7.20.3.4) يضمن أنه سينجح دائمًا:

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

لو ptr هو مؤشر فارغ ، reallocتتصرف الوظيفة مثل mallocوظيفة للحجم المحدد. خلاف ذلك ، إذا ptr لا يتطابق مع مؤشر تم إرجاعه في وقت سابق من قبل calloc, malloc, ، أو realloc الوظيفة ، أو إذا تم تعامل المساحة من خلال دعوة إلى free أو reallocالوظيفة ، السلوك غير محدد. إذا كان لا يمكن تخصيص ذاكرة الكائن الجديد ، فإن الكائن القديم لم يتم تعويضه ولم يتغير قيمته.

عائدات

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

تطبيق مطابق بسيط للغاية realloc سيكون هذا:

void *realloc(void *ptr, size_t size)
{
    void *new_ptr= malloc(size);
    if (new_ptr && ptr)
    {
        size_t original_size= _get_malloc_original_size(ptr);
        memcpy(new_ptr, ptr, min(original_size, size));
        free(ptr);
    }

    return new_ptr;
}

في ظل ظروف الذاكرة المنخفضة (أو أي شروط بموجبها malloc سيعود NULL) ، هذا سيعود NULL.

سيكون أيضًا تحسينًا بسيطًا جدًا لإرجاع نفس المؤشر إذا كان حجم التخصيص الأصلي أكبر من أو يساوي الحجم المطلوب. ولكن لا شيء في المعيار C يملي ذلك.

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

يقول C99 القياسي §7.20.3.4 (RealLoc):

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

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

عائدات

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

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

الحجة العادية العادية هي "إذا لم يكن هذا لا يمكن أن يفشل ، فهذا يعني أن لدي مسار خطأ لا يمكنني اختباره". إلى حد ما ، هذا صحيح. ومع ذلك ، يمكن أن يكون هناك بعض الدس للذاكرة بحيث لا يمكن أن ينجح التخصيص - لأن معلومات التحكم قد تالفة. على الأرجح ستحصل على تفريغ أساسي ، ولكن ربما يكون الرمز قويًا بما يكفي لتكون قادرًا على تجنب ذلك. (أفترض أن الشفرة الصلبة التي تم ترميزها 100 و 50 هي لأغراض طرح السؤال ؛ لن يتم التخلص من رمز الحياة الحقيقية عندما يعرف كم يحتاج إليه حقًا.)

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

بخصوص "تحرير 2" ...

قد يكون الرمز أفضل مكتوبة على النحو التالي:

if (b != NULL)
    a = b;
return a;

لكن المفهوم الأساسي على ما يرام. لاحظ أن المعيار يقول صراحة أن التخصيص الأصلي آمن إذا كان لا يمكن إنشاء المعيار الجديد.

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

realloc() يمكن العودة NULL بسهولة كافية على الحد من الحجم.

void *ptr = malloc(10);
ptr = realloc(ptr, 0);
if (ptr == NULL) {
  puts("Failure because return value is NULL? - not really");
}

realloc(any_pointer, 0) يمكن أن تعود NULL أو ربما بعض not-NULL مؤشر ، هو المحدد.

هذا هو السبب realloc()/malloc() يجب ألا يكون الفشل اختبارًا بسيطًا لـ if (ptr == NULL) لكن

void *ptr = malloc(newsize); // or realloc(..., newsize)
if (ptr == NULL && newsize > 0) {
  exit(0); // Handle OOM;
}

بسبب هذا الغموض ، يجب أن يرغب الرمز في جعل ملف realloc() غلاف ، أوصي بشيء مثل:

void *xrealloc(void *ptr, size_t newsize, bool *falure) {
  *failure = 0;
  if (newsize > 0) {
    void *tmp = realloc(ptr, newsize);
    if (tmp == NULL) {
      *failure = 1;
      return ptr;  // old value
    }
    return tmp;  // new value
  } 
  free(ptr);
  return NULL; // new value
  }

الحصول NULL على realloc() مع انخفاض الحجم ، لذلك ليس حقًا بالفشل وهكذا تنطبق هذه الإجابة بشكل عرضي فقط ، ولكن سؤال OP كان "... فرض التحقق من RealLoc ما إذا كان حجم الكتلة الجديد أصغر من الأولي؟" ثم استخدم أقل جدارة بالثقة if (ptr == NULL) نموذج.

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