لماذا أتلقى مزدوجة مجانا الخطأ مع realloc()?

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

  •  08-06-2019
  •  | 
  •  

سؤال

لقد حاولت أن أكتب سلسلة استبدال وظيفة في C الذي يعمل على char *, التي تم تخصيصها باستخدام malloc().الأمر مختلف قليلا في أنها سوف تجد محل سلاسل, بدلا من الأحرف في بداية السلسلة.

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

ربما قليلا كود تساعد:

void strrep(char *input, char *search, char *replace) {
    int searchLen = strlen(search);
    int replaceLen = strlen(replace);
    int delta = replaceLen - searchLen;
    char *find = input;

    while (find = strstr(find, search)) {

        if (delta > 0) {
            realloc(input, strlen(input) + delta);
            find = strstr(input, search);            
        }

        memmove(find + replaceLen, find + searchLen, strlen(input) - (find - input));
        memmove(find, replace, replaceLen);
    }
}

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

إذا كان ذلك يساعد ، رمز الدعوة تبدو مثل:

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

void strrep(char *input, char *search, char *replace);

int main(void) {
    char *input = malloc(81);

    while ((fgets(input, 81, stdin)) != NULL) {
        strrep(input, "Noel", "Christmas");
    }
}
هل كانت مفيدة؟

المحلول

كقاعدة عامة, يجب أن أبدا هل حرة أو realloc على المستخدم توفير العازلة.كنت لا تعرف أين المستخدم تخصيص مساحة (في الوحدة النمطية الخاص بك في آخر DLL) لذا لا يمكنك استخدام أي من توزيع المهام على المستخدم العازلة.

شريطة أن كنت الآن لا تستطيع أن تفعل أي تخصيص في وظيفة الخاص بك, يجب عليك تغيير سلوكها مثل فعل واحد فقط استبدال, وبالتالي فإن المستخدم سوف تكون قادرة على حساب السلسلة الناتجة ماكس طول توفر لك مع العازلة طويلة بما يكفي استبدال تحدث.

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

مما أدى إلى:

void  strrep(char *input, char *search, char *replace);
char* strrepm(char *input, char *search, char *replace);
void  strrepmfree(char *input);

نصائح أخرى

أولا آسف أنا في وقت متأخر إلى الحزب.هذا هو أول ستاكوفيرفلوو الإجابة.:)

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

الإجابة OP, realloc() بإرجاع مؤشر حديثا-تخصيص الذاكرة.قيمة الإرجاع يجب أن يتم تخزينها في مكان ما.عموما, يمكنك أن تفعل هذا:

data *foo = malloc(SIZE * sizeof(data));
data *bar = realloc(foo, NEWSIZE * sizeof(data));

/* Test bar for safety before blowing away foo */
if (bar != NULL)
{
   foo = bar;
   bar = NULL;
}
else
{
   fprintf(stderr, "Crap. Memory error.\n");
   free(foo);
   exit(-1);
}

كما TyBoer ، يا رفاق لا يمكن تغيير قيمة المؤشر التي يتم تمريرها في كإدخال إلى هذه الوظيفة.يمكنك تعيين ما تريد, ولكن التغيير سوف تخرج من نطاق في نهاية الدالة.في ما يلي كتلة "الإدخال" قد تكون أو لا تكون مؤشر غير صالح مرة واحدة وظيفة يكمل:

void foobar(char *input, int newlength)
{
   /* Here, I ignore my own advice to save space. Check your return values! */
   input = realloc(input, newlength * sizeof(char));
}

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

هل يمكن استخدام مزدوج مؤشر المدخلات مثل هذا:

void foobar(char **input, int newlength)
{
   *input = realloc(*input, newlength * sizeof(char));
}

إذا كان الطالب لديه نسخة مكررة من إدخال المؤشر في مكان ما ، أن تكرار لا يزال قد تكون غير صالحة الآن.

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

مجرد طلقة في الظلام لأنني لم أجربها بعد ولكن عندما realloc يعود المؤشر مثل malloc.لأن realloc يمكن تحريك المؤشر إذا لزم الأمر كنت على الأرجح تعمل على مؤشر غير صالح إذا كنت لا تفعل ما يلي:

input = realloc(input, strlen(input) + delta);

شخص آخر اعتذر عن التأخر حزب شهرين ونصف مضت.حسنا, لقد قضيت الكثير من الوقت في القيام البرامج الآثار.

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

قبل إجراء التحليل ، أنا أتفق مع أولئك الذين يقولون واجهة الخاص بك هو أقل من ممتاز;ومع ذلك ، إذا كنت التعامل مع تسرب الذاكرة/الدوس القضايا الموثقة 'يجب أن تكون الذاكرة المخصصة متطلب, أنها يمكن أن تكون 'موافق'.

ما هي المشاكل ؟ حسنا, يمكنك تمرير العازلة إلى realloc () ، realloc() ترجع لك مؤشر جديد إلى المنطقة يجب استخدام - و أنت تتجاهل أن قيمة الإرجاع.وبالتالي realloc() قد تحررت الأصلي الذاكرة ، ثم تمر عليه نفس المؤشر مرة أخرى ، وأنه يشكو أنك تحرير نفس الذاكرة مرتين لأنك تمرير القيمة الأصلية إلى ذلك مرة أخرى.هذا ليس فقط تسرب الذاكرة ، ولكن يعني أن كنت الاستمرار في استخدام الفضاء الأصلي -- جون داوني طلقة في الظلام يشير إلى أن كنت إساءة استخدام realloc () ، ولكن لا أؤكد بشدة كيف كنت تفعل ذلك.هناك أيضا من جانب واحد خطأ لأنك لا تخصيص مساحة كافية NUL '\0' أن ينهي السلسلة.

حدوث تسرب للذاكرة لأنك لا توفر آلية تخبر المتصل عن القيمة الأخيرة من السلسلة.لأنك حافظت يدوس على السلسلة الأصلية بالإضافة إلى الفضاء بعد ذلك ، يبدو أن الكود يعمل لكن إذا دعوتكم قانون تحرير الفضاء ، فإنه أيضا سوف تحصل على ضعف خالية من الخطأ أو أنها قد تحصل على تفريغ الأساسية أو ما يعادلها لأن الذاكرة التحكم في المعلومات سارعت تماما.

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

إذا التنقيح المقترح من دعا وظيفة:

char *strrep(char *input, char *search, char *replace) {
    int searchLen = strlen(search);
    int replaceLen = strlen(replace);
    int delta = replaceLen - searchLen;
    char *find = input;

    while ((find = strstr(find, search)) != 0) {
        if (delta > 0) {
            input = realloc(input, strlen(input) + delta + 1);
            find = strstr(input, search);            
        }

        memmove(find + replaceLen, find + searchLen, strlen(input) + 1 - (find - input));
        memmove(find, replace, replaceLen);
    }

    return(input);
}

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

ملاحظة محاولة تحرير التعليمات البرمجية الخاصة بك للتخلص من html رموز الهروب.

حسنا, على الرغم من أنها كانت فترة من الوقت منذ أن كنت تستخدم C/C++ ، realloc التي تنمو فقط إعادة استعمال الذاكرة مؤشر القيمة إذا كان هناك غرفة في الذاكرة بعد كتلة الأصلي.

على سبيل المثال النظر في هذا:

(xxxxxxxxxx..........)

إذا كان المؤشر يشير إلى أول العاشر .يعني مجانا موقع الذاكرة و تكبر حجم الذاكرة إلى قبل متغير من 5 بايت, انها سوف تنجح.وهذا بالطبع مثال مبسط كما كتل تقريب تصل إلى حجم معين المحاذاة ، ولكن على أية حال.

ومع ذلك ، إذا كنت في وقت لاحق في محاولة لتنمو من خلال آخر 10 بايت, و هناك فقط 5 المتاحة, سوف تحتاج إلى تحريك كتلة في الذاكرة وتحديث المؤشر الخاص بك.

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

هذا المؤشر قيمة ، ومع ذلك ، تم تحرير.

في حالة الإدخال هو الجاني.

ومع ذلك, وأود أن أقدم اقتراحا آخر.في هذه الحالة يبدو أن المدخلات المتغير هو في الواقع المدخلات ، و إذا كان كذلك, فإنه لا ينبغي أن يكون تعديل على الإطلاق.

وهكذا حاول أن تجد طريقة أخرى أن تفعل ما تريد القيام به ، من دون تغيير المدخلات, كما الآثار الجانبية مثل هذا يمكن أن يكون من الصعب تعقب.

يبدو أن هذا العمل ؛

char *strrep(char *string, const char *search, const char *replace) {
    char *p = strstr(string, search);

    if (p) {
        int occurrence = p - string;
        int stringlength = strlen(string);
        int searchlength = strlen(search);
        int replacelength = strlen(replace);

        if (replacelength > searchlength) {
            string = (char *) realloc(string, strlen(string) 
                + replacelength - searchlength + 1);
        }

        if (replacelength != searchlength) {
            memmove(string + occurrence + replacelength, 
                        string + occurrence + searchlength, 
                        stringlength - occurrence - searchlength + 1);
        }

        strncpy(string + occurrence, replace, replacelength);
    }

    return string;
}

تنفس الصعداء ، هناك على أية حال إلى رمز آخر دون هذا المستوى الرديء ؟

realloc غريبة ومعقدة يجب أن يستخدم فقط عند التعامل مع الكثير من الذاكرة الكثير من المرات في الثانية الواحدة.أي- حيث أنه في الواقع يجعل التعليمات البرمجية الخاصة بك بشكل أسرع.

لقد رأيت البرمجية حيث

realloc(bytes, smallerSize);

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

دائما استخدام قيمة الإرجاع من realloc.

بلدي سريعة تلميحات.

بدلا من:
void strrep(char *input, char *search, char *replace)
محاولة:
void strrep(char *&input, char *search, char *replace)

و من في الجسم:
input = realloc(input, strlen(input) + delta);

عموما قرأت عن تمرير دالة الحجج القيم/المرجعية realloc() الوصف :).

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