C语言 使用strtok_r时指针无效



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

我阅读了关于strtok_r的文档,但我不明白为什么分配"str"To a new 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("%sn", 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("%sn", token); 
}

free(str);
return (0); 
}

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

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

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

因此,使用辅助变量rest允许在指针str中保留初始值。

注意你调用的函数不正确。下面是它的描述

第一次调用strtok_r()时,str应该指向待调用的字符串解析,忽略saveptr的值。在随后的调用中,str应该是NULL,saveptr应该从之前的电话。

因此,对于该函数的第二次及后续调用,第一个实参应为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("%sn", token); 
}

return (0); 
}
  • 首先,不需要为str分配内存,因为可以定义一个本地数组来存储数据。您可以使用sizeof操作符,这样如果您决定更改str的大小,就不会有在两个地方不更新它的风险。在使用malloc的情况下,你最好在#define中设置一个常量来保存值,而你在使用分配的缓冲区大小的任何地方使用该常量。
  • ,从不强制转换malloc的返回值。相信我,这是一个非常坏的习惯。当你做强制类型转换时,你告诉编译器你知道你在做什么。转换malloc的值是C语言中没有void类型时遗留下来的(到目前为止是80年代中期)。曾几何时,malloc()返回的char *通常不是你想要的指针类型,你必须强制转换指针以匹配你正在使用的指针。在2021年铸造malloc()返回值不仅不推荐,但它是强烈不鼓励的,因为许多错误来自于铸造它(编译器警告你,当你做坏事,但它不会,如果你铸造值,通常这被解释为你告诉编译器你是故意做一些奇怪的事情,所以编译器关闭,并没有说更多)
  • 第三,如果你要提取字符串中的所有令牌,第一次你需要调用strtok()(或他的朋友strtok_w),第一个参数指向字符串的开始,,但其余的调用必须使用NULL作为它的第一个参数,或者在刚刚返回的字符串中搜索,而不是在第一次发生之后。您的问题不是关于使用strtokstrtok_r,因为strtok_r只是strtok的可重入版本,允许您在第一个线程内启动嵌套循环,或者从不同的线程调用它。

堆内存管理跟踪实现库调用的基内存地址。我们需要保留这些基地址,以便在必要时释放/重新分配。

既然你找到了使用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 ("%sn", token);
}
/* Original string is chopped up with NULCHAR, now unreliable */
}

最新更新