【问题标题】:Using strtok() on an allocated string?在分配的字符串上使用 strtok()?
【发布时间】:2010-12-07 14:35:31
【问题描述】:

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

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

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

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

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

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

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

“地址 0x5179450 是一个大小为 38 的块中的 8 个字节”

或类似的东西。

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

【问题讨论】:

标签: c string free malloc


【解决方案1】:

你问:

有什么我应该知道的吗 在分配的字符串上使用 strtok?

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

其次,您拨打free() 的次数必须与拨打malloc()calloc() 的次数一样多(但realloc() 可能会影响计数)。

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

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

除非Parse() 分配它保留的空间,否则在调用free() 之后,您不能使用dest 结构(或更准确地说,指向dest 结构中的行的指针)。 free() 释放由getline() 分配的空间,之后对指针的任何使用都会产生未定义的行为。请注意,未定义的行为包括“看似有效,但只是巧合”的选项。

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

Parse() 调用 strtok() 一个变量 在线次数,并做了一些 验证。每个令牌(指向 strtok()) 返回的内容被放入 排队,直到我知道我有多少 有。

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

然后将它们复制到 malloc'd char** in dest.

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

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

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

或类似的东西。

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

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

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

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

请注意,您需要释放每个单独分配的 (strdup()'d) 字符串以及通过 dest 访问的字符指针数组。

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

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 我现在正在对每个令牌进行 strdup,然后再存储。我现在没有内存释放错误,生活很美好。
      • 如果这回答了您的问题,请点击“接受”复选标记。对于您在此处提出的其他问题,最好这样做。如果有更好的答案,您可以稍后撤消它。
      • 不是标准的 C 函数。
      • strdup() 如果你没有它很容易实现:snipplr.com/view/16919/strdup
      • strdup() 是一个 POSIX 标准函数......标准的好处是有很多可供选择。
      【解决方案4】:

      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 步中犯了错误。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-07
        • 2020-07-19
        • 1970-01-01
        • 2014-01-21
        相关资源
        最近更新 更多