سؤال

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

string new = make("Hello, ");
adds(new, "everyone");
free(new);

تعمل التعليمة البرمجية المذكورة أعلاه - وهي تجمع و يعمل بشكل جيد.رمز أدناه لا يعمل - وهي تجمع أشواط ثم

string new = make("Hello, ");
adds(new, "everyone!");
free(new);

الفرق بين المدونة هو أن adds() وظيفة إضافة 1 المزيد من حرف (أ !).حرف ويضيف أنه لا فرق - فقط على طول.فقط من أجل اكتمال التعليمات البرمجية التالية لا تعمل:

string new = make("Hello, ");
adds(new, "everyone");
adds(new, "!");
free(new);

الغريب التعليمة البرمجية التالي الذي يستخدم وظيفة مختلفة ، addc() (الذي يضيف 1 حرف بدلا من سلسلة) يعمل:

string new = make("Hello, ");
adds(new, "everyone");
addc(new, '!');
free(new);

التالية ، والتي أيضا يفعل الشيء نفسه ، يعمل:

string new = make("Hello, everyone!");
free(new);

الخطأ أن جميع تلك التي لا تعمل هو هذا:

test(526) malloc: *** error for object 0x100130: double free
*** set a breakpoint in malloc_error_break to debug

(test هو غاية وصفي اسم البرنامج لدي هذا في.)

بقدر ما وظيفة الداخلية ، make() هو دعوة إلى strlen() واثنين من المكالمات malloc() ودعوة إلى memcpy(), ، adds() هو دعوة إلى strlen(), دعوة إلى realloc(), و دعوة إلى memcpy(), ، free() هو مكالمتين إلى المكتبة القياسية free().

هل هناك أي أفكار لماذا أنا على الحصول على هذا, أو هل أنا بحاجة إلى كسر و استخدام مصحح أخطاء?أنا فقط الحصول على ذلك مع adds()es من خلال مدة معينة ، addc()s.

كسر ونشر مدونة وظائف:

typedef struct _str {
  int _len;
  char *_str;
} *string;

string make(char *c)
{
    string s = malloc(sizeof(string));
    if(s == NULL) return NULL;
    s->_len = strlen(c);
    s->_str = malloc(s->_len + 1);
    if(s->_str == NULL) 
      {     
        free(s);
        return NULL;
      }
    memcpy(s->_str, c, s->_len);
    return s;
}

int adds(string s, char *c)
{
    int l = strlen(c);
    char *tmp;
    if(l <= 0) return -1;
    tmp = realloc(s->_str, s->_len + l + 1);
    if(!tmp) return 0;
    memcpy(s->_str + s->_len, c, l);
    s->_len += l;
    s->_str[s->_len] = 0;
    return s->_len;
}

void myfree(string s)
{
    if(s->_str) free(s->_str);
    free(s);
    s = NULL;
    return;
}
هل كانت مفيدة؟

المحلول

عدد من المشاكل المحتملة أود أن الإصلاح:

1/ الخاص بك make() أمر خطير لأنه ليس من النسخ في جميع أنحاء null-منهي عن السلسلة.

2/ كما أنه لا معنى لتعيين s إلى NULL في myfree() منذ انها مرت المعلمة و لن يكون له أي تأثير على المعلمات الفعلية مرت في.

3/ أنا لست متأكدا لماذا العودة -1 من adds() إذا وأضاف طول السلسلة هو 0 أو أقل.أولا, لا يمكن أن تكون سلبية.ثانيا يبدو معقولا تماما أنك يمكن أن تضيف سلسلة فارغة ، والتي ينبغي أن يؤدي عدم تغيير السلسلة والعودة الحالية طول السلسلة.وأود أن العودة فقط طول -1 إذا كان فشل (أي realloc() لم ينجح) وتأكد من السلسلة القديمة محفوظة إذا حدث ذلك.

4/ أنت لست تخزين tmp متغير في s->_str على الرغم من أنه يمكن تغيير ذلك نادرا إعادة تخصيص الذاكرة في المكان إذا كنت زيادة حجم على الرغم من أنه من الممكن إذا كانت الزيادة هي صغيرة بما يكفي لتناسب في أي مساحة إضافية المخصصة من قبل malloc().الحد من حجم المؤكد أن إعادة تخصيص في مكان إلا إذا كان تنفيذ malloc() استخدامات مختلفة العازلة تجمعات مختلفة الحجم كتل الذاكرة.ولكن هذا مجرد جانبا ، منذ كنت لا من أي وقت مضى تقليل استخدام الذاكرة مع هذا القانون.

5/ أعتقد محددة المشكلة هنا هو أن كنت فقط تخصيص مساحة السلسلة وهو مؤشر إلى الهيكل ، وليس الهيكل نفسه.وهذا يعني عندما تضع السلسلة في إفساد الذاكرة الساحة.

هذا هو رمز كتبت (بما في ذلك أكثر وصفية أسماء المتغيرات ، ولكن هذا مجرد تفضيل بلدي).

لقد تغيرت:

  • عودة القيم من adds() لتعكس على نحو أفضل طول والخطأ الظروف.الآن فقط يعود -1 إذا لم تستطع توسيع (و سلسلة الأصلي هو يمسها) - أي قيمة الإرجاع هو طول السلسلة.
  • عودة من myfree() إذا كنت تريد حقا ترغب في تعيين سلسلة فارغة مع شيء من هذا القبيل "s = myfree (s)".
  • الشيكات myfree() بالنسبة NULL السلسلة حيث يمكنك الآن لم تخصص string دون تخصيص string->strChars.

هنا هو استخدام (أو لا :-) على النحو الذي تراه مناسبا:

/*================================*/
/* Structure for storing strings. */

typedef struct _string {
    int  strLen;     /* Length of string */
    char *strChars;  /* Pointer to null-terminated chars */
} *string;

/*=========================================*/
/* Make a string, based on a char pointer. */

string make (char *srcChars) {
    /* Get the structure memory. */

    string newStr = malloc (sizeof (struct _string));
    if (newStr == NULL)
        return NULL;

    /* Get the character array memory based on length, free the
       structure if this cannot be done. */

    newStr->strLen = strlen (srcChars);
    newStr->strChars = malloc (newStr->strLen + 1);
    if(newStr->strChars == NULL) {     
        free(newStr);
        return NULL;
    }

    /* Copy in string and return the address. */

    strcpy (newStr->strChars, srcChars);
    return newStr;
}

/*======================================================*/
/* Add a char pointer to the end of an existing string. */

int adds (string curStr, char *addChars) {
    char *tmpChars;

    /* If adding nothing, leave it alone and return current length. */

    int addLen = strlen (addChars);
    if (addLen == 0)
        return curStr->strLen;

    /* Allocate space for new string, return error if cannot be done,
       but leave current string alone in that case. */

    tmpChars = malloc (curStr->strLen + addLen + 1);
    if (tmpChars == NULL)
        return -1;

    /* Copy in old string, append new string. */

    strcpy (tmpChars, curStr->strChars);
    strcat (tmpChars, addChars);

    /* Free old string, use new string, adjust length. */

    free (curStr->strChars);
    curStr->strLen = strlen (tmpChars);
    curStr->strChars = tmpChars;

    /* Return new length. */

    return curStr->strLen;
}

/*================*/
/* Free a string. */

string myfree (string curStr) {
    /* Don't mess up if string is already NULL. */

    if (curStr != NULL) {
        /* Free chars and the string structure. */

        free (curStr->strChars);
        free (curStr);
    }

    /* Return NULL so user can store that in string, such as
       <s = myfree (s);> */

    return NULL;
}

الوحيد الممكن تحسين أرى أن الحفاظ على العازلة من الفضاء و نهاية strChars للسماح مستوى التوسع دون استدعاء malloc().

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

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

نصائح أخرى

ويجب أن يكون malloc الأول في make:

malloc (sizeof (struct _str));

وإلا كنت فقط تخصيص مساحة كافية ل<م> المؤشر إلى struct _str.

 tmp = realloc(s->_str, s->_len + l + 1);

وrealloc يمكن إرجاع مؤشر جديد إلى كتلة المطلوبة. تحتاج إلى إضافة السطر التالي من التعليمات البرمجية:

 s->_str = tmp;

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

وحاول تغيير إضافة النحو التالي

 tmp = realloc(s->_str, s->_len + l + 1);
 if (!tmp) return 0;
 if (tmp != s->_str) {
     printf("Block moved!\n"); // for debugging
     s->_str = tmp;
 }

في وظيفة adds، التي تفترض أن realloc لا يغير عنوان كتلة الذاكرة التي تحتاج إلى إعادة تخصيص:

tmp = realloc(s->_str, s->_len + l + 1);
if(!tmp) return 0;
memcpy(s->_str + s->_len, c, l);

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

memcpy(s->_str + s->_len, c, l);

ربما يجب إضافة رمز ، ولكن مزدوج مجانا يعني كنت تتصل مجانا على نفس المؤشر مرتين.

  1. يتم إضافة 1 إلى strlen عن \0 بايت في نهاية المطاف ؟
  2. بمجرد مجانا مؤشر هل وضع متغير عضو NULL بحيث لا مجانا مرة أخرى (أو سيئة معروفة مؤشر مثل 0xFFFFFFFF)

وماذا يفعل "بلدي مجانا () لا مكالمتين إلى المكتبة القياسية مجانية ()." لماذا يدعون لك مجانا مرتين؟ يجب عليك سوى الاتصال مرة واحدة.

يرجى الرد يضيف الخاص بك ()؛ ومجانا () وظائف.

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