先说说一个使用strtok经常遇见的问题:
char *str = "Fred John Micheal";
char buf[4];
int i = 0;
while((buf[i] = strtok(str, " ")) != NULL)
{
i++;
str = NULL;
}
上面这段代码在运行的时候会出现Segment fault,原因是strtok函数会改变第一个参数的值,而str是一个常量,所以会出错。
如果将str的定义做以下改变:
char str[100] = "Fred John Micheal";
结果就会正确了。
第一种声明方式虽然声明的也是一个字符串,str这个指针是放在堆栈区的,但是字符串的内容却是放在静态区的,所以是常量不能改变。
下面介绍一下strtok和strtok_r的实现原理:
1.strtok
函数原型:
char *strtok(char *str1, char *str2);
strtok函数在实现的过程中使用了一个静态变量,在第一次截取了分隔符前面的字符串并返回以后,函数会修改原始字符串将分隔符位置的字符修改为"/0",静态变量会指向其后面的字符串的首字母。以后每次再截取的时候第一个参数设为NULL,就会不断的截取。
但是strtok函数的一个缺点就是修改了原始的字符串,将分隔符替换为了"/0",这也就是为什么函数的第一个参数不能为const的原因。而这个函数也因此不可重入,并且不是线程安全的。为此又有了可重入的版本,即strtok_r。
2.strtok_r
函数原型为:
char *strtok_r(char *s, const char *delim, char **ptrptr);
其中ptrptr变量替代了strtok函数中的静态变量的作用,用来传递一个指针的引用,也就是说截取以后生下字符串的首字母地址,因此不同的使用过程定义不同的指针变量传递进去就可以了,下面是个例子可以很清楚的理解:
#include<stdio.h>
#include<string.h>
#define INFO_MAX_SZ 255
int main()
{
int in=0;
char buffer[INFO_MAX_SZ]="Fred male 25,John male 62,Anna female 16";
char *p[20];
char *buf=buffer;
char *outer_ptr=NULL;
char *inner_ptr=NULL;
while((p[in]=strtok_r(buf,",",&outer_ptr))!=NULL) {
buf=p[in];
while((p[in]=strtok_r(buf," ",&inner_ptr))!=NULL) {
in++;
buf=NULL;
}
p[in++]="***";
buf=NULL; }
printf("Here we have %d strings/n",i);
for (int j=0; jn<i; j++)
printf(">%s</n",p[j]);
return 0;
}
这一次的输出为:
Here we have 12 strings
>Fred<
>male<
>25<
>***<
>John<
>male<
>62<
>***<
>Anna<
>female<
>16<
>***<
让我来分析一下以上代码的运行过程:
红色为strtok_r的outer_ptr指向的位置,
紫色为strtok_r的inner_ptr指向的位置,
蓝色为strtok对字符串的修改
1. "Fred male 25,John male 62,Anna female 16" //外循环
2. "Fred male 25/0John male 62,Anna female 16"//进入内循环
3. "Fred/0male 25/0John male 62,Anna female 16"
4 "Fred/0male/025/0John male 62,Anna female 16"
5 "Fred/0male/025/0John male 62,Anna female 16" //内循环遇到"/0"回到外循环
6 "Fred/0male/025/0John male 62/0Anna female 16"//进入内循环
以上内容参考:
http://blog.csdn.net/zhongnanhai/archive/2009/10/06/4637272.aspx