【问题标题】:How to use strtok in C properly so there is no memory leak?如何在 C 中正确使用 strtok 以防止内存泄漏?
【发布时间】:2011-06-27 09:10:46
【问题描述】:

当您在 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);
}

进一步假设“line”在传递给 myFunc 之前已被分配。我应该在使用 strtok 后释放原始字符串还是为我们完成这项工作?另外,如果'line'没有被分配并且我尝试使用上面的函数会发生什么?改为执行以下操作更安全吗? (假设程序员知道该行没有被分配就不会调用 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
}

【问题讨论】:

    标签: c malloc free strtok


    【解决方案1】:

    值得解释的是,strtok 通过以下方式完成工作:

    1. 返回指向原始字符串的指针;和

    2. 将找到的每个分隔符替换为 NULL。

    因此,一切就位,无需分配任何内存。

    【讨论】:

      【解决方案2】:

      strtok 不会比 strlen 释放更多的内存。你为什么会期望它?它会释放什么内存?也许你认为 strtok 需要释放内存是因为它存储了一个 NUL,但内存的内容是无关紧要的。当您分配内存时,分配器会跟踪您分配的块的大小,并在您释放它时释放整个块。

      【讨论】:

        【解决方案3】:

        在您问题的评论中,您说您“多次调用'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 的工作方式解释起来有点复杂,但您已经掌握了重要的部分——它不分配或释放任何内存。相反,它通过修改您传递给它的字符串来工作。

        【讨论】:

        • +1 表示 strtok(LINE, "<token>") 不起作用,除非所有后续调用 strtok 都接收 NULL 作为参数:strtok(NULL, "<token>");
        【解决方案4】:

        strtok() 不会释放任何东西,因为它不知道字符串的存储位置。它可能在堆栈或堆上,它不知道也不关心! :)

        改为执行以下操作更安全吗?

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

        进一步阅读: strtok()

        【讨论】:

        • 请注意,第二种方法可以简化为myFunc(line); free(line); - 因为指针line 是按值传递给myFunc(),所以临时是不必要的。
        • @caf 当然,您假设 OP 实际上分配了行,但这在代码中并不明显。鉴于关于内存分配和释放的明显混淆,该假设可能不成立。
        【解决方案5】:

        这与strtok() 有什么关系?如果分配内存,则需要释放它。您的应用程序决定分配和释放内存的位置取决于您。但是,如果您将内存传递给strtok(),则无论何时分配或释放内存都没有区别。

        【讨论】:

        • 我在这两种方法中释放内存的方式是否正确?我想知道的是 strtok 是否隐式地释放内存。请记住,我不是这门语言的专家。
        • @user246392:正如我在回答中所说,调用strtok() 对分配的内存没有任何影响。它不分配或释放任何内存。它与分配或释放内存的问题无关。通常,从分配它的同一例程中释放内存是有意义的,但如果这不适合您的应用程序,那么真正重要的是它会被释放。
        • @user246392 无法确定您是否正确释放内存,因为我们无法知道您是如何分配它的。我可以说你几乎肯定做错了。
        猜你喜欢
        • 1970-01-01
        • 2015-01-31
        • 1970-01-01
        • 1970-01-01
        • 2019-10-17
        • 2010-12-20
        • 1970-01-01
        • 2010-12-02
        相关资源
        最近更新 更多