【问题标题】:What's the difference between strtok_r and strtok_s in C?C中的strtok_r和strtok_s有什么区别?
【发布时间】:2012-02-19 17:54:28
【问题描述】:

我正在尝试在需要能够在 Linux 和 Windows 中编译的 C 程序中使用此函数。起初我尝试使用 strtok_r,但是当我在 windows 上编译时,它抱怨该函数不存在并说它会假定它是一个外部函数,但后来失败了。然后我使用了 strtok_s 并编译了它!然后我在 Linux 上尝试过,但现在它抱怨有一个“未定义的对‘strtok_s’的引用”。

一个是只有windows的功能,另一个是linux的功能???我该怎么做才能让它同时编译?

【问题讨论】:

  • strtok_r() 是 POSIX; strtok_s() 是 Windows。使用普通的strtok(),它在所有实现中都可用(支持C89)
  • 问题是我需要 strtok 一个字符串同时 strtok-ing 另一个字符串,这就是为什么我需要这个,因为它保存状态。
  • @pmg - 除非您需要线程或缓冲区溢出安全
  • @MartinBeckett 等等什么?它在哪里拯救国家?只有两个参数,一个是 NULL(在第二个标记上),另一个是您要查找的字符。我希望能够strtok一个字符串,在该字符串中找到一些东西并strtok那个,然后回到原始字符串并继续strtok它。
  • 这是一个古老的问题,但我觉得有必要指出 strtok_s 是 C11,而不仅仅是 Windows。

标签: c windows linux compiler-errors strtok


【解决方案1】:

strtok_r 是 POSIX 系统上 strtok 的线程安全版本

strtok_s 是 Windows 上 strtok 的缓冲区溢出安全版本。 windows 上的标准 strtok 是线程安全的,所以 strtok_s 应该是。

【讨论】:

  • strtok_r 是 POSIX 的一部分,并且已经存在多年(几十年?)
  • 我仍然认为答案并不是非常有用,因为它只是涵盖了 OP(大部分)已经知道的内容,而不是如何解决问题。
  • @R - 解决方案是您可以在 Posix 上使用 strok_r,在 Windows 上使用 strtok_s,Joachim 的解决方案描述了如何使用。或者您可以仅使用 Posix 子系统对窗口进行编程
  • @MartinBeckett 它是 C11 Annex K 标准,而不仅仅是 Windows :)
  • @AlexanderRiccio Microsoft strtok_s 和 C11 strtok_s 完全不同!微软strtok_s只有3个参数,而C11strtok_s有4个参数。微软strtok_s的原型是char* strtok_s(char* str, const char* delimiters, char** context);,而C11strtok_s的原型是char *strtok_s(char *restrict str, rsize_t *restrict strmax, const char *restrict delim, char **restrict ptr);
【解决方案2】:

这两个函数都非常丑陋、不直观,用于解析字符串,并且通常无法以微妙的方式满足您特定应用程序的要求。对于标准 C 中的普通 strtok 更是如此。只需将它们扔掉并编写您自己的代码来迭代 char 数组并根据需要将其分解。 strchrstrspnstrcspn 可以帮助您执行此操作,或者您可以从头开始处理阵列。

【讨论】:

  • 你能解释一下为什么这些功能不好吗?对我来说,这似乎比逐字逐句地搜索更直观。
  • (1) 分隔符字符被覆盖/丢失,因此除非您只有一个可能的分隔符,否则您将丢失信息。 (2) 多个连续的分隔符被视为一个分隔符,如果您的分隔符是空格,但如果它是逗号则没有意义,例如..
  • 请注意,在 some 情况下,strtok 语义正是您想要的,唯一的问题是状态性(strtok_rstrtok_s 修复)。我只是发现每次我尝试使用它们时,至少有一个方面的解析需求它们不支持,最终直接编写代码会更容易。
  • -1:为了解析已读入临时缓冲区的字符串,您想要丢弃分隔符并且只希望字符串在完成解析之前有效,strtok 正是您所需要的want 和 strtok_r/strtok_s 更好,因为它是可重入的。
【解决方案3】:

strtok_s 只是 strtok_r 的 Windows 版本,在其他任何地方都是标准的。

当涉及到像strtok_s/strtok_r 这样的函数时,使程序可移植的一种(我认为很常见的)方法是使用预处理器:

#if defined(_WIN32) || defined(_WIN64)
/* We are on Windows */
# define strtok_r strtok_s
#endif

由于原型和功能相同,您现在只能使用strtok_r

【讨论】:

  • 如果在 cygwin 上编译它可能会中断,该 cygwin 将自己报告为 Windows,但已经定义了像 strtok_r 这样的 posix 接口。使用#ifndef HAVE_STRTOK_R 之类的东西(并在构建脚本中检测它)会更好。
  • @R..:cygwin 实际上并没有定义_WIN32(或_WIN64)正是出于这个原因——它通常用于识别非unix 的代码。 Cygwin 改为定义 __CYGWIN____unix__
  • 很高兴知道 C11 中还有另一个 strtok_s 具有完全不同的签名:en.cppreference.com/w/c/string/byte/strtok
  • @ChrisDodd MinGW 上仍有问题。 MinGW 预定义了_WIN32,但它支持strtok_r。我认为最好检查 _MSC_VER 宏。
【解决方案4】:

我没有足够的声誉来评论其他答案,所以我必须提供自己的答案。

1) 解决这个问题:

"strtok_s 是 Windows 上 strtok 的缓冲区溢出安全版本。 Windows 上的标准 strtok 是线程安全的..."

这不是真的。 strtok_s 是 MSVC 编译器的线程安全版本。 strtok 不是线程安全的!

2) 解决这个问题:

"如果在报告自身的 Cygwin 上编译,这可能会中断 作为 Windows,但已经定义了像 strtok_r 这样的 POSIX 接口。”

再一次,不是真的。不同之处在于您使用的编译器。当使用 Microsoft 的 Visual C++ 编译器 MSVC 时,函数为 strtok_s. 另一个编译器,例如 GNU Compiler Collection,GCC,可能使用不同的标准库实现,例如 strtok_r。在确定要使用的函数时考虑编译器,而不是目标平台。

在我看来,Joachim Pileborg 的回答是这一页上最好的回答。然而,它需要一个小的编辑:

#if defined(_WIN32) /* || defined(_WIN64) */
#define strtok_r strtok_s
#endif

_WIN32 和 _WIN64 都是 MSVC 编译器提供的预定义宏。 _WIN64 在编译 64 位目标时定义。 _WIN32 是为 32 位和 64 位目标定义的。这是微软为向后兼容而做出的妥协。创建 _WIN32 是为了指定 Win32 API。现在您应该考虑使用 _WIN32 来指定 Windows API——它不是特定于 32 位目标的。

【讨论】:

  • 第一点你错了:strtok is 在 Windows 上是线程安全的,因为两个不同的线程可以在同一个进程中安全地使用该函数,即使 strtok 有其他线程@JameyKirby 在another answer 中解释的问题。
  • MinGW 预定义了_WIN32,但它支持strtok_r,所以最好检查_MSC_VER 宏。
【解决方案5】:

只是为了澄清。 strtok 在 Windows 中是线程安全的。 strtok 使用TLS 变量来维护每个线程的最后一个指针。但是,您不能使用strtok 在每个线程中交错访问多个令牌字符串。 strtok_rstrtok_s 都通过允许用户通过第三个参数维护上下文来解决此交错问题。希望这会有所帮助。

【讨论】:

  • 虽然对正在寻找可移植解决方案的 OP 没有帮助,但这对我找出为什么我的遗留代码在 Linux 上崩溃但在 Windows 上没有崩溃非常有帮助。因为我认为我知道 strtok 的行为方式,所以我并不关心彻底阅读 Windows strtok 文档......
【解决方案6】:

MinGW 还预定义了_WIN32,但它支持strtok_r,所以我认为检查_WIN32 宏不是一个好主意。最好查看_MSC_VER 宏,它是 Microsoft Visual Studio 的宏。

#ifdef _MSC_VER
#define strtok_r strtok_s
#endif

警告:Microsoft strtok_s 和 C11 strtok_s 完全不同! Microsoftstrtok_s只有3个参数,而C11strtok_s有4个参数,以后可能会兼容问题。

微软strtok_s的原型是

char* strtok_s(char* str, const char* delimiters, char** context);

C11strtok_s的原型是

char *strtok_s(char *restrict str, rsize_t *restrict strmax, const char *restrict delim, char **restrict ptr);

【讨论】:

    猜你喜欢
    • 2014-04-08
    • 2014-03-01
    • 2011-04-05
    • 2011-10-18
    • 1970-01-01
    • 2010-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多