【问题标题】:Which is most standard: strnlen or strnlen_s?哪个是最标准的:strnlen 或 strnlen_s?
【发布时间】:2021-02-24 07:34:17
【问题描述】:

在我当前的项目中,我正在根据 C11 标准(使用 gcc -std=c11 构建)进行编码,并且需要类似 strnlenstrlen 的“安全”版本,它返回以 0 结尾的字符串的长度) ,但只能达到给定的最大值)。所以我查了一下(例如https://en.cppreference.com/w/c/string/byte/strlen),似乎C11标准提到了这样一个功能,但名称为strnlen_s

因此我选择了strnlen_s,但是当包含string.h 时,结果是未定义的。另一方面,strnlen 是定义的,所以我目前的解决方案是使用strnlen,并注明标准名称似乎是strnlen_s,但这不是 GCC 定义的。

问题是:我是否正确假设 strnlen 是最便携的名称,或者我可以做些什么来使代码最便携/标准?

注意:Microsoft (https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strnlen-strnlen-s) 实现这两个函数的区别在于,strnlen_s 检查字符串指针是否为 NULL 并在这种情况下返回 0,而 strnlen 没有这样的检查。

【问题讨论】:

  • 2021年,UTF-8 is everywhere,所以你可以使用GNU libunistringGlib
  • @BasileStarynkevitch 对于许多需要 UTF-aware 的应用程序来说,这是一个很好的观点。我知道 UTF,但我当前的应用程序不需要。顺便说一句,您指向“UTF-8 无处不在”的链接中缺少一个“y”。
  • 是的,正确的网站是utf8everywhere.org

标签: c c11


【解决方案1】:

问题是:我是否正确假设 strnlen 是最便携的名称,或者我可以做些什么来使代码最便携/标准?

不,它根本不便携。它从来都不是 C 的一部分。它包含在 POSIX 中,这并不意味着什么。

我可以想象标准中不存在该功能的原因,可能是因为当我们已经有memchr(str, '\0', max);时它是多余的。

strnlen_s 是 C11 附件 K 中可选的边界检查接口的一部分。整章都变成了一个巨大的失败,几乎没有任何编译器实现它。 Microsoft 有类似的命名函数,但它们有时不兼容。所以我会假设所有_s 函数都是完全不可移植的。

所以不要使用这些,使用memchrstrlen


编辑

如果您出于某种原因必须自己实现strnlen,那么我建议您这样做:

#include <string.h>

size_t strnlength (const char* s, size_t n) 
{ 
  const char* found = memchr(s, '\0', n); 
  return found ? (size_t)(found-s) : n; 
}

【讨论】:

  • memchr 是一个不错的选择。在像我这样的情况下不太方便(字符串有一个固定大小的缓冲区,如果它比缓冲区大小短,则只需要以 0 结尾)。可能我最便携的选择是编写自己的“strnlen”,可能包含对memchr 的调用。
  • 编译器不实现_s函数的原因是什么?为什么它是可选的?
  • @nielsen 或者干脆不包装它并保持代码可读。写char* result = memchr(str, '\0', n); if(result){ ptrdiff_t index = str - result; } 并没有这么大的努力。无论您做什么,都需要检查结果。
  • @Lundin 是的,但就我而言,它更多。类似:char* result = memchr(str, '\0', n); if(result) {size = result - str;} else {size = n;}size = (result) ? result - str : n;。我在几个地方都需要它。无论如何,这是一个偏好和权衡的问题。幸运的是,它不是很复杂。
【解决方案2】:

strnlen_s() 在 C 标准的附录 K 中指定,从版本 C11 开始。这个附件没有被广泛实施,甚至微软的实施也不完全符合指定的版本。语义是扭曲的,尤其是在错误处理方面。我建议不要使用它。

strnlen() 是 POSIX.1-2008 中指定的简单函数,可在许多平台上使用。在不提供它的平台上很容易实现:

#include <string.h>

size_t strnlen(const char *s, size_t n) {
    size_t i;
    for (i = 0; i < n && s[i] != '\0'; i++)
        continue;
    return i;
}

【讨论】:

  • 但是如果你用这个名字定义一个函数,它会在有strnlen的系统上引起冲突。所以,也许它的名字不同
  • 或者如 cmets 中对另一个答案所述,将其实现为:size_t strnlen (const char* s, size_t n) { char* found = memchr(s, '\0', n); return found ? (size_t)(found-s) : n; }。而这个sn-p其实是库质量代码。
【解决方案3】:

问题是:我是否正确假设 strnlen 是最便携的名称,或者我可以做些什么来使代码最便携/标准?

对于 C,strnlen 可以,因为名称没有保留。它不是标准的一部分,所以你可以添加。

POSIX reserves str...(),因此您可能需要使用其他名称。

strnlen_sK.3.7.4.4 的 strnlen_s 函数 发生冲突,并且有一段有争议的历史,您可能不希望您的代码与之相关。避免将函数命名为 strnlen_s()


我会避免使用一个添加两个名称的任何函数与通用库的名称联盟:正式的不太可能发生冲突的名称和宏

size_t nielsen_strnlen(const char *s, size_t maxsize);
#define slength nielsen_strnlen

或者干脆直接用不太可能发生碰撞的东西去。

size_t nstrnlen(const char *s, size_t maxsize);

更深入:OP 似乎想要使用标准 C 库(或当前版本)之外的流行函数,但在将代码移植到其他系统时可能可用。 OP 希望提供一个 use-my-code-if-not-available 功能。

小心你踩的地方。

我会使用宏(或包装函数)

#if ON_SYSTEM_WITH_strnlen
  #define slength strnlen
#else
  #define slength nielsen_strnlen
#endif   

...然后调用slenth()

当 OP 的代码版本与期望的(今天和明天)不完全 或者因为它不是标准 时出现问题,各种实现会有所不同 - 有点,关于其实施。为了减轻影响,请考虑使用宏或函数包装器间接。


附带问题:参数顺序和潜在的new principle to the "original principles" of C

size_t foo1(const char *s, size_t maxsize);

// arranged such that the size of an array appears before the array. 
size_t foo2(size_t maxsize, const char *s);
size_t foo3(size_t maxsize, const char s[maxsize]); 

【讨论】:

  • 您提出了一些很好的观点,但我最初的问题是关于使用最有可能在其他环境中可用的功能。只有在讨论之后,我才意识到最好的选择可能是自己实现它(以确保它始终可用)。在这种情况下,我完全同意应该选择名称以避免冲突,因为您解释得很好。
【解决方案4】:

string 是 c++ 头文件,而 string.h 是 c 头文件(至少在 gcc 中是这样)。 strlen_s (afaik) 是Microsoft extension to the C library。没错, strlen 会更标准。如果需要字节数,也可以使用 memchr。就@Basile 而言,如果您需要字符数,则需要支持 UTF-8 的内容。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-13
    • 2013-07-26
    • 2020-11-01
    • 2019-10-21
    • 2015-05-20
    • 2023-03-25
    • 2011-05-29
    • 1970-01-01
    相关资源
    最近更新 更多