如何正确使用C语言中的strtok函数以避免内存泄漏?

14

当我在C语言中调用一个char指针的strtok函数时,我有些困惑它会发生什么。我知道它会修改字符串的内容,所以如果我在名为'line'的变量上调用strtok,它的内容将会改变。假设我按照下面的方法进行:

void function myFunc(char* line) {

    // get a pointer to the original memory block
    char* garbageLine = line;

    // Do some work
    // Call strtok on 'line' multiple times until it returns NULL
    // Do more work

    free(garbageLine);
}
进一步假设在传递给myFunc之前,'line'已经被malloc分配了。在使用strtok后,我是否应该释放原始字符串,还是它会自动完成这个任务?如果'line'没有被malloc分配,并且我尝试使用上面的函数,会发生什么?是否更安全地执行以下操作?(假设程序员知道该行未被malloc分配,因此不会调用free)
char* garbageLine = line;
myFunc(line);
free(garbageLine);

函数定义

void function myFunc(char* line) {
    // Do some work
    // Call strtok on 'line' multiple times until it returns NULL
    // Do more work
}
5个回答

9

strtok()函数不会释放任何内存,因为它不知道字符串存储在哪里。它可能在栈上或堆上,它不知道也不关心! :)

使用以下方法是否更安全?

你的第二个示例更好,因为它简化了myFunc()函数,并使其在更多情况下有用,因为该函数不需要知道字符串分配的位置。通过从myFunc()函数中移除free()函数调用,您可以使用该函数来解析来自栈或堆的字符串。调用者分配内存,调用者释放内存!

进一步阅读:strtok()


请注意第二种方法可以简化为 myFunc(line); free(line); - 临时变量是不必要的,因为指针 line 通过值传递给了 myFunc() - caf
@caf,你假设OP实际上分配了line的内存,但从代码中并不能明确得出。考虑到关于内存分配和释放的混淆不清,这种假设可能是不合理的。 - Jim Balter

6
值得解释的是,strtok 通过以下方式完成其工作:
  1. 返回指向原始字符串内部的指针;并且

  2. 用 NULL 替换它找到的每个分隔符字符。

因此,一切都在原地进行,它不需要分配任何内存。

4
在你的问题的评论中,你说:“对'line'调用strtok多次直到它返回NULL”. 这听起来好像你可能在错误地使用strtok。第一次调用它时,应该将'line'作为参数调用;在随后的调用中,应该传递NULL。以下是一个示例:
void function myFunc(char* line) {
  char *segment; // This will point at each delimited substring in turn.

  segment = strtok(line, " ");

  // Do something with segment.

  segment = strtok(NULL, " ");

  // Do something with the new segment.

  free(line);
}

正如DrTwox所说,你的第二个例子更好 - “line”应该由分配它的上下文释放(或不释放),因此在此函数中调用free()是不必要的。而你最好使用循环 - 类似于:

void function myFunc(char* line) {
  char *segment;

  segment = strtok(line, " ");

  while (segment != NULL) {
    // Do something with segment.

    segment = strtok(NULL, " ");
  }
}

调用方式如下:

char *line = malloc(20*sizeof(char));

// Check that malloc succeeded here.
// Put some data into 'line'.

myFunc(line);

free(line);

// No 'garbageLine' required.

strtok的工作原理有些复杂,但你已经掌握了重要部分——它不会分配或释放任何内存。相反,它通过修改你传递给它的字符串来工作。


如果没有所有后续调用strtok都将NULL作为参数传递,那么strtok(LINE,“<token>”)是无法正常工作的。正确的用法是:strtok(NULL, "<token>")。请注意这一点。 - Alexej Magura

0
这与 strtok() 有什么关系呢?如果你分配了内存,就需要释放它。你的应用程序决定在哪里分配和释放内存是由你决定的。但是,如果你将内存传递给 strtok(),那么无论何时或何地分配或释放内存都没有任何影响。

这两种方法中我释放内存的方式正确吗?我的问题是strtok是否隐式地释放了内存。请记住,我不是该语言的专家。 - user246392
正如我在答案中所说的,调用strtok()对分配的内存没有任何影响。它不会分配或释放任何内存。这与分配或释放内存的问题无关。通常情况下,从分配内存的相同例程中释放内存是有意义的,但如果这不适合您的应用程序,真正重要的是它被释放了。 - Jonathan Wood
@user246392 无法确定您是否正确释放内存,因为我们无法知道您是如何分配它的。但我可以肯定地说,您几乎肯定是做错了。 - Jim Balter

0

strtok函数释放的内存不会比strlen函数多。你为什么会期望它会呢?它会释放哪些内存呢?也许你认为strtok需要释放内存是因为它存储了一个NUL,但是内存的内容并不重要。当你分配内存时,分配器会跟踪你分配的块的大小,并且在你释放它时整个块都会被释放。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接