C内存分配问题

时间:2009-03-16 00:57:11

标签: c malloc double-free

所以我有几个函数可以使用我创建的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()的两次调用。< / p>

有没有想法为什么我得到这个,或者我需要分解并使用调试器?我只使用超过一定长度的adds()来获取它,而不是使用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;
}

6 个答案:

答案 0 :(得分:4)

make中的第一个malloc应为:

malloc (sizeof (struct _str));

否则,您只需为指针分配足够的空间到struct _str

答案 1 :(得分:4)

我要解决的一些潜在问题:

1 /你的make()是危险的,因为它没有复制到字符串的空终止符。

2 /在s中将NULL设置为myfree()也没有意义,因为它是传递的参数,对传入的实际参数没有影响。

3 /如果添加的字符串长度为0或更小,我不确定为什么从adds()返回-1。首先,它不能是消极的。其次,你可以添加一个空字符串似乎很合理,这应该导致不更改字符串并返回当前字符串长度。如果失败,我只返回-1的长度(即realloc()不起作用)并确保在发生这种情况时保留旧字符串。

4 /你没有将tmp变量存储到s->_str中,即使它可以改变 - 如果你增加大小,它很少就地重新分配内存尽管有可能增量小到足以容纳malloc()分配的任何额外空间。除非malloc()的实现对不同大小的内存块使用不同的缓冲池,否则减小大小几乎肯定会就地重新分配。但这只是一个旁边,因为你不会通过这个代码减少内存使用量。

5 /我认为你的特定的问题是你只为字符串分配空间,字符串是指向结构的指针,而不是结构本身。这意味着当你输入字符串时,你就会破坏内存领域。

这是我写的代码(包括更多描述性的变量名,但这只是我的偏好)。

我改变了:

  • 来自adds()的返回值,以更好地反映长度和错误条件。现在它只返回-1,如果它不能展开(原始字符串是不变的) - 任何其他返回值是新的字符串长度。
  • myfree()返回,如果您真的想要将字符串设置为NULL,请使用“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()函数来减少具有大缓冲区和小字符串的字符串。

答案 2 :(得分:2)

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

realloc可以返回指向所请求块的新指针。您需要添加以下代码行:

 s->_str = tmp;

在一种情况下它没有崩溃但在添加一个字符之后没有崩溃的原因可能仅仅是因为内存的分配方式。可能存在最小分配增量(在这种情况下为16)。因此,当您为hello分配前8个字符时,它实际上会分配16个。当您添加所有人时,它不会超过16,因此您将获得原始块。但对于17个字符,realloc返回一个新的内存缓冲区。

尝试按以下方式更改添加<​​/ p>

 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;
 }

答案 3 :(得分:1)

在函数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);

答案 4 :(得分:0)

可能应该发布代码,但是double free意味着你在同一个指针上自由地调用两次。

  1. 你是否在末尾的\ 0字节中为strlen添加1?
  2. 释放指针后,是否将成员变量设置为NULL,以便不再释放(或者使用已知的错误指针,如0xFFFFFFFF)

答案 5 :(得分:0)

为什么“我的free()是对标准库free()的两次调用。”你为什么两次免费打电话?你应该只需要打一次电话。

请发布你的add();和free()函数。