首页 > 解决方案 > strtok_r 保存状态行为

问题描述

正确的使用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);

这种方法有什么缺点吗?我是否导致任何未定义的行为?我尝试了一些极端情况,两种方法的工作方式相似。

在线编译器:https ://wandbox.org/permlink/rkGiwXOUTzqrbMpP

PS 为简洁起见忽略内存泄漏。


更新

已经存在一个与我的as_tokens: strsep几乎相似的函数。在有连续分隔符的情况下有所不同。strsep返回一个空字符串,而as_tokens(即 strtok_r)将它们视为一个。

标签: ctokenizestrtokstrsep

解决方案


这种方法有什么缺点吗?

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

此外,正如评论中所观察到的,它不符合规范,即使用第一个参数strtok_r调用的行为仅在先前调用的上下文中定义,该调用提供了第三个参数指向的值。strtok_rNULLstrtok_r

此外,它偏离了惯用的、易于理解的 . 用法strtok_r,甚至将其隐藏在不同的函数中。正常的成语不繁琐,人尽皆知。聪明一点会让你的代码更难维护。

我是否导致任何未定义的行为?

是的,在“未定义的行为”的意义上,与明确称为未定义的行为相反。但相关标准赋予这些替代品同样的意义。看上面。


推荐阅读