使用 strtok_r 时指针无效

invalid pointer when using strtok_r

当 运行 我的代码(显示在第一个代码块中)时,出现此错误: *** Error in `./a.out': free(): invalid pointer: 0x0000000001e4c016 *** 我找到了一个修复程序(显示在第二个代码块中),但我不明白为什么会首先发生错误。

我阅读了有关 strtok_r 的文档,但我不明白为什么将“str”分配给新的 char* 可以解决问题。

“rest = str”不是说rest和str指向同一个内存块吗?这如何解决问题???

损坏的代码:

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h>

int main() 
{ 
    char* str = (char*) malloc(sizeof(char) * 128);
    char* token;
    
    printf("Enter something: ");  
    fgets(str, 128, stdin);
  
    while ((token = strtok_r(str, " ", &str))) { 
        printf("%s\n", token); 
    }
    
    free(str);
    return (0); 
}

固定码:

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h>

int main() 
{ 
    char* str = (char*) malloc(sizeof(char) * 128);
    char* token; 
    char* rest = str; 
    
    printf("Enter something: ");  
    fgets(str, 128, stdin);
  
    while ((token = strtok_r(rest, " ", &rest))) { 
        printf("%s\n", token); 
    }
    
    free(str);
    return (0); 
}

很明显,strtok_r 的调用更改了作为第三个参数通过引用传递给调用的指针 str

while ((token = strtok_r(str, " ", &str))) { 
                                   ^^^^
    printf("%s\n", token); 
}

所以在函数调用后指针str可以指向原始字符串内部。因此它不会存储调用 malloc.

后的值

因此使用辅助变量rest可以让指针保持初始值str.

注意你调用的函数有误。这是它的描述

On the first call to strtok_r(), str should point to the string to be parsed, and the value of saveptr is ignored. In subsequent calls, str should be NULL, and saveptr should be unchanged since the previous call.

因此对于函数的第二次和后续调用,第一个参数应为 NULL

你应该写:

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h>

int main() 
{ 
    char  str[128];
    char *token; 
    char *rest = str; 
    
    printf("Enter something: ");  
    fgets(str, sizeof str, stdin);
  
    for (token = strtok_r(rest, " ", &rest);
         token = strtok_r(NULL, " ", &rest);
         /* just nothing here */)
    { 
        printf("%s\n", token); 
    }
    
    return (0); 
}
  • 首先,您不需要为str分配内存,因为您可以定义一个本地数组来存储数据。您可以使用 sizeof 运算符,这样如果您决定更改 str 的大小,就不会 运行 在两个地方不更新它的风险。在使用 malloc 的情况下,你最好 #define 一个常量来保存值,而你在使用分配缓冲区大小的任何地方都使用常量。
  • 其次,从不 转换 malloc 的 returned 值。相信我,这是一个非常糟糕的习惯。当你进行强制转换时,你告诉编译器你知道你在做什么。转换 malloc 的值是 C 中没有 void 类型时的遗留问题(这是八十年代中期的事)。曾几何时,malloc() 用于 return 一个通常不是您想要的指针类型的 char *,您必须转换指针以匹配您正在使用的指针。不仅不建议在 2021 年转换 malloc() return 值,而且强烈建议不要这样做,因为转换它会导致许多错误(编译器会在您做错事情时警告您,但它不会,如果你转换值,通常这会被解释为你告诉编译器你故意做一些奇怪的事情,所以编译器闭嘴,不再多说)
  • 第三,如果要提取一个字符串中的所有token,第一次需要调用strtok()(或者他的朋友strtok_w),第一个参数指向start字符串的 但其余调用必须使用 NULL 作为第一个参数 完成,否则您将在字符串中搜索 returned,而不是在第一次出现之后。您的问题不在于使用 strtokstrtok_r,因为 strtok_r 只是 strtok 的可重入版本,它允许您在第一个内部开始嵌套循环,或者从不同的线程调用它。

堆内存管理跟踪用于实现库调用的基本内存地址。我们需要在必要时将这些基地址保存到 free/reallocate。

既然您找到了使用 strtok_r() 的方法,我更喜欢以下版本:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main () {
    char orgStr [] = "strtok does not allow you to have 2 pointers going at once on the same string";

    for (char *token, *rmdStr = orgStr; token = strtok_r (NULL, " ", &rmdStr); /* empty */) {
        printf ("%s\n", token);
    }
    /* Original string is chopped up with NULCHAR, now unreliable */
}