0

关于在 malloced 字符串上使用 strtok 有什么我应该知道的吗?

在我的代码中,我有(一般而言)

char* line=getline();
Parse(dest,line);
free(line);

wheregetline()是一个将 char * 返回到某个 malloced 内存的函数。并且Parse(dest, line)是一个在线解析的函数,将结果存储在dest, (之前已经部分填充,来自其他信息)。

Parse()在线调用strtok()可变次数,并进行一些验证。每个令牌(一个指向返回的指针strtok())都被放入一个队列中,直到我知道我有多少。

然后将它们复制到 dest 中的 malloc'd char** 上。

现在free(line) 还有一个释放 dest 中 char*[] 的每个部分的函数,两者都出现在 valgrind 上:

“地址 0x5179450 是一个大小为 38 的块内的 8 个字节 free'd”

或类似的东西。

我正在考虑重构我的代码,不要直接将令牌存储在 char** 上,而是存储它们的副本(通过将空间 == 分配给 strlen(token)+1,然后使用strcpy())。

4

4 回答 4

2

然后将它们复制到 dest 中的 malloc'd char**。

复制字符串,还是复制指针?该strtok函数会修改您提供给它的字符串,以便它可以为您提供指向同一字符串的指针,而无需复制任何内容。当您从中获得令牌时,您必须复制它们。只要任何令牌指针正在使用中,要么保留输入字符串。

许多人建议您strtok完全避免,因为它容易出错。此外,如果您使用线程并且 CRT 不支持线程,strtok则可能会使您的应用程序崩溃。

于 2009-10-17T11:19:49.767 回答
2

有一个函数strdup分配内存,然后将另一个字符串复制到其中。

于 2009-10-17T12:49:10.053 回答
2

你问:

关于在 malloced 字符串上使用 strtok 有什么我应该知道的吗?

有很多事情需要注意。首先,strtok()在处理字符串时修改字符串,'\0'在找到分隔符的地方插入空值 ( )。这不是分配内存的问题(这是可修改的!);如果您尝试将常量字符串传递给strtok().

free()其次,你必须有尽可能多的调用malloc()calloc()(但realloc()可能会打乱计数)。

在我的代码中,我有(一般而言)

   char* line=getline();
   Parse(dest,line);
   free(line);

除非Parse()分配它保留的空间,否则destdest调用free(). free()释放分配的空间以及getline()之后对指针的任何使用都会产生未定义的行为。请注意,未定义的行为包括“看似有效,但只是巧合”的选项。

其中 getline() 是一个将 char * 返回到某个 malloced 内存的函数,而 Parse(dest, line) 是一个在线解析的函数,将结果存储在 dest 中(之前已部分填充,来自其他信息)。

Parse() 在线调用 strtok() a 的次数不定,并进行一些验证。每个令牌(指向 strtok() 返回的指针)都被放入队列中,直到我知道我有多少。

请注意,返回的指针strtok()都是指向由 分配的单个空间块的指针getline()。您没有描述任何额外的内存分配。

然后将它们复制到 dest 中的 malloc'd char** 上。

这听起来好像您将指针从strtok()指针数组中复制出来,但您并没有注意复制这些指针指向的数据。

现在 free(line) 和一个释放 dest 中 char*[] 的每个部分的函数,两者都出现在 valgrind 上:

"Address 0x5179450 is 8 bytes inside a block of size 38 free'd"

或类似的东西。

free()' char *[]' 部分的第一个dest可能有一个指向line并因此释放整个内存块的指针。部分的所有后续释放dest都试图释放未返回的地址malloc(),并valgrind试图告诉你这一点。然后free(line)操作失败,因为第一个free()指针dest已经释放了该空间。

我正在考虑重构我的代码 [以] 存储它们的副本 [...]。

提议的重构可能是明智的;其他人已经提到的功能strdup()将整齐可靠地完成工作。

请注意,重构之后,您仍然需要释放行,但不会释放任何返回的指针strtok()。它们只是指向由(标识)管理的空间的指针,line并且将在您释放时全部释放line

请注意,您需要释放每个单独分配的 ( strdup()'d) 字符串以及通过dest.

或者,不要在呼叫后立即释放线路Parse()。记录dest分配的指针 ( line) 并在释放指针数组时释放它。但是,您仍然没有释放由 返回的指针strtok()

于 2009-10-17T20:04:47.110 回答
0

1 在您的 parse() 中,strtok() 仅在每个匹配位置写入 '\0'。其实这一步没什么特别的。使用 strtok() 很容易。当然它不能用于只读内存缓冲区。

2 对于在 parse() 中获得的每个子字符串,将其相应地复制到 malloc()ed 缓冲区。如果我给出一个存储子字符串的简单示例,它看起来像下面的代码,从概念上说,尽管它可能与您的真实代码不完全相同:

    字符 **dest;
    dest = (char**)malloc(N * sizeof(char*));
    对于(我:0..N-1){
        dest[i] = (char*)malloc(LEN);
        strcpy(dest[i], sub_strings[i]);
    注意:上面 2 行可能只是下面的一行
        dest[i] = strdup(sub_string[i]);
    }

3 free dest,再次从概念上讲:

    对于(我:0..N-1){
        免费(目的地[i]);
    }
    免费(目的地);

4 call free(line) 也没什么特别的,它甚至不会影响你的“dest”。

“dest”和“line”使用不同的内存缓冲区,因此如果愿意,您可以在第 3 步之前执行第 4 步。如果您遵循上述步骤,则不会发生错误。似乎您在代码的第 2 步中犯了错误。

于 2009-10-17T15:10:32.827 回答