0

所以我有几个函数可以与string我创建的类型一起使用。其中之一创建了一个动态分配的字符串。另一个接受所述字符串,并扩展它。最后一个释放字符串。注意:函数名称已更改,但都是我自定义的。

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

上面的代码有效 - 它编译并运行良好。下面的代码不起作用 - 它编译、运行,然后

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

代码之间的区别在于该adds()函数增加了 1 个字符 (a !)。它添加的字符没有区别 - 只是长度。为了完整起见,以下代码不起作用:

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是我在其中的程序的极具描述性的名称。)

就函数内部而言,mymake()是一个调用strlen()和两个调用malloc()和一个调用memcpy(),myadds()是一个调用strlen()、一个调用realloc()和一个调用memcpy(),而 myfree()是对标准库的两个调用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;
}
4

6 回答 6

4

第一个 mallocmake应该是:

malloc (sizeof (struct _str));

否则,您只会为指向struct _str.

于 2009-03-16T01:19:45.590 回答
4

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

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

s2/ 设置为NULLin也没什么意义,myfree()因为它是一个传递的参数,不会对传入的实际参数产生影响。

adds()3/如果添加的字符串长度为 0 或更少,我不确定为什么返回 -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()函数来减少具有大缓冲区和小字符串的字符串。

于 2009-03-16T01:21:33.370 回答
2
 tmp = realloc(s->_str, s->_len + l + 1);

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

 s->_str = tmp;

它在一种情况下不会崩溃但在添加一个字符后会崩溃的原因可能只是因为内存的分配方式。可能有一个最小分配增量(在本例中为 16)。因此,当您为 hello 分配前 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;
 }
于 2009-03-16T01:51:33.127 回答
1

在 functionadds中,您假设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);
于 2009-03-16T10:06:37.960 回答
0

可能应该发布代码,但是双重释放意味着你在同一个指针上调用了两次 free。

  1. 您是否将 1 添加到 strlen 最后的 \0 字节?
  2. 一旦你释放了一个指针,你是否将你的成员变量设置为 NULL,这样你就不会再次释放(或已知的错误指针,如 0xFFFFFFFF)
于 2009-03-16T01:01:46.523 回答
0

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

请张贴您的 add(); 和 free() 函数。

于 2009-03-16T01:07:02.767 回答