C strtok_r保存状态行为

C strtok_r保存状态行为,c,tokenize,strtok,strsep,C,Tokenize,Strtok,Strsep,使用strtok_r的方法如下所示: char* str = strdup(string); char* save; char* ptr = strtok_r(str, delim, &save); while(ptr) { puts(ptr); ptr = strtok_r(NULL, delim, &save); } 当我试图检查save中实际存储的内容时,我发现它只是未解析字符串的其余部分。所以我试着让第二个调用看起来像第一个调用,并编写了一个包装器,如下所示 c

使用strtok_r的方法如下所示:

char* str = strdup(string);
char* save;
char* ptr = strtok_r(str, delim, &save);
while(ptr) {
  puts(ptr);
  ptr = strtok_r(NULL, delim, &save);
}
当我试图检查
save
中实际存储的内容时,我发现它只是未解析字符串的其余部分。所以我试着让第二个调用看起来像第一个调用,并编写了一个包装器,如下所示

char* as_tokens(char** str, const char* const delim) {
  return strtok_r(NULL, delim, str);
}
这可以像下面这样使用,它的详细程度要低得多。我们不必区分第一次呼叫和其他呼叫

char* str = strdup(string);
char* ptr;
while(ptr = as_tokens(&str, delim))
  puts(ptr);
这种方法有什么缺点吗?我是否造成任何未定义的行为?我尝试了一些edge案例,两种方法的效果相似

联机编译器:

为了简洁起见,请忽略内存泄漏


更新

已经存在一个几乎与my
as_令牌类似的函数:。
当存在连续分隔符时,情况有所不同
strep
返回一个空字符串,而
as_令牌
(即strtok_r)将它们视为一个

这种方法有什么缺点吗

是的,它会丢失
str
的原始值,因此无法(在本例中)释放它。因此,您存在内存泄漏。这可以通过保留指针的一个单独副本来解决,但这可以归结为与您的第一个代码几乎相同的事情

此外,如评论中所述,它不符合
strtok_r
的规范,因为第一个参数
NULL
调用
strtok_r
的行为仅在提供第三个参数指向的值的先前调用
strtok_r
的上下文中定义

此外,它还偏离了人们熟知的习惯用法strtok_r
,甚至将其隐藏在不同的函数中。正常的习语并不繁重,它是众所周知的,也能被理解。对它的巧妙处理会使代码更难维护

我是否造成任何未定义的行为

是的,在“未定义的行为”的意义上,与被明确称为未定义的行为相反。但相关标准将同样的重要性归于这些替代方案。见上文

这种方法有什么缺点吗

是的,它会丢失
str
的原始值,因此无法(在本例中)释放它。因此,您存在内存泄漏。这可以通过保留指针的一个单独副本来解决,但这可以归结为与您的第一个代码几乎相同的事情

此外,如评论中所述,它不符合
strtok_r
的规范,因为第一个参数
NULL
调用
strtok_r
的行为仅在提供第三个参数指向的值的先前调用
strtok_r
的上下文中定义

此外,它还偏离了人们熟知的习惯用法strtok_r,甚至将其隐藏在不同的函数中。正常的习语并不繁重,它是众所周知的,也能被理解。对它的巧妙处理会使代码更难维护

我是否造成任何未定义的行为


是的,在“未定义的行为”的意义上,与被明确称为未定义的行为相反。但相关标准将同样的重要性归于这些替代方案。参见上文。

我认为,strtok_r文档中没有指定保存状态的方式,因此依赖它会有风险。设计者可以指定实现以您想要的方式工作,但选择不这样做。当
str
为空时,您违反了先前调用中未修改
saveptr
的要求。违反的要求实际上是“在第一次调用strtok_r()时,str应该指向要解析的字符串”,但我猜这里的问题恰恰是“它可以被违反吗?”我认为一般的答案是“否”,理论上,一个实现可以在第一次调用期间(当
str
不是
NULL
时)索引整个字符串,而
save
将指向索引数据,而不仅仅是字符串的尾部。编写自己的
strtok_r
实现更容易、更安全,它将100%以这种方式运行,然后依赖于不确定的数据。我相信
strtok_r
文档中没有指定保存状态的方式,因此依赖它是有风险的。设计者可以指定实现以您想要的方式工作,但选择不这样做。当
str
为空时,您违反了先前调用中未修改
saveptr
的要求。违反的要求实际上是“在第一次调用strtok_r()时,str应该指向要解析的字符串”,但我猜这里的问题恰恰是“它可以被违反吗?”我认为一般的答案是“否”,理论上,一个实现可以在第一次调用期间(当
str
不是
NULL
时)索引整个字符串,而
save
将指向索引数据,而不仅仅是字符串的尾部。编写自己的
strtok_r
实现更容易、更安全,它将100%以这种方式运行,然后依赖于不确定的数据。