【问题标题】:strtok_r save state behaviourstrtok_r 保存状态行为
【发布时间】:2019-07-14 15:39:56
【问题描述】:

correctstrtok_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

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


更新

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

【问题讨论】:

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

标签: c tokenize strtok strsep


【解决方案1】:

这种方法有什么缺点吗?

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-26
    相关资源
    最近更新 更多