C语言strtok函数工作原理及常见使用问题答疑
问题1:为什么将line-id(01)处代码改为printf("%s",*p)会触发段错误?p是指针,不是应该用*p获取指向的值吗?
printf的%s格式符要求传入的参数是char类型指针,指向待打印字符串的首地址,printf会从这个地址开始逐字符读取,直到遇到\0停止。p本身就是char*类型,值就是字符串首地址,直接传p才符合%s的要求。而*p是对指针解引用,拿到的是p指向的第一个字符(比如第一次调用后*p就是字符h,对应的ASCII码值是十进制104),把这个数值当成内存地址传给%s时,printf会尝试访问0x68(即十进制104)这个低地址空间,该空间属于操作系统保护区域,用户进程无访问权限,因此触发段错误。
如果要打印单个字符,应该用%c格式符,此时才需要传*p。
问题2:为什么取消line-id(00)处注释执行printf("%s",a)时仅输出hey?
strtok是原地修改输入字符串的函数:第一次调用strtok(a,"-")时,会从数组a的首地址开始查找第一个分隔符-,找到后会把该位置的-替换为字符串结束符\0。printf用%s输出数组a时,会从首地址开始读,遇到第一个\0就终止输出,因此只会输出\0之前的内容,也就是第一个切分结果hey(根据原字符串定义可能附带后面的空格)。
问题3:strtok函数中指针p的作用,以及strtok的完整工作机制
指针p的作用
strtok每次调用的返回值是当前切分出的子串(token)的首地址,如果没有更多可切分的子串则返回NULL。指针p的作用就是接收这个返回值,用来遍历所有切分得到的子串。
strtok完整工作机制
strtok内部维护了一个静态全局指针,用来记录上次切分后剩余字符串的起始位置,工作流程分为两类调用场景:
- 首次调用:第一个参数传入待切分字符串的首地址,第二个参数传入分隔符集合。
- 从传入的字符串首地址开始,先跳过所有属于分隔符集合的字符,找到第一个非分隔符的位置作为当前token的首地址
- 继续向后查找,直到找到第一个属于分隔符集合的字符,将该字符替换为
\0 - 把内部静态指针更新为该分隔符后面的第一个字符的地址
- 返回当前token的首地址
- 后续调用:第一个参数传入
NULL,第二个参数可以和之前相同或者更换分隔符集合。- 直接从内部静态指针记录的位置开始,重复上面的查找、替换、更新静态指针的逻辑,返回当前token的首地址
- 如果查找至字符串末尾
\0仍未找到可切分的token,则返回NULL
补充说明:
- strtok会修改原字符串,因此不能传入只读的字符串常量作为切分对象,否则会触发段错误
- 内部静态指针的存在导致strtok不可重入,不能同时对多个字符串进行切分操作,多线程环境下也存在线程安全问题,这种场景建议使用可重入版本
strtok_r
你提供的代码中第二次切分输出不符合预期,就是因为第一次切分已经把原字符串的第一个-替换成了\0,第二次调用strtok(a,"*")时,扫描到这个\0也没找到*,只能返回第一个tokenhey,后续没有更多有效内容就返回NULL结束循环。
内容的提问来源于stack exchange,提问作者Spark
相关产品推荐
相关产品推荐

