C/C++:errno关联字符串的最大大小(编译时)

C/C++: maximum size of errno-associated strings (at compile-time)

本文关键字:编译 C++ errno 字符串 关联      更新时间:2023-10-16

问题

有没有办法在编译时(在预处理器时会更好)获得与errno相关的任何字符串的最大大小?例如strlen(strerror(errno))的上界?

我的想法

我能想到的最好的方法是运行一个程序,在int的范围内,在每个区域设置上进行强力搜索,获取与每个{errno,locale}对相关的字符串,获取其大小,并在该系统上生成一个标头,然后将其挂接到例如makefile或autoconf或其他文件中。我想不出更好的方法来做到这一点,但这样做似乎很荒谬:系统的标准库内置了这些信息,哪怕只是隐含的。真的没有好的方法来获取这些信息吗?

好吧,我承认C和/或C++标准可能允许在运行时生成错误字符串,例如特定于环境的消息(例如strerror(EINVAL)给出了上次设置errno时从其他运行时元数据集派生的字符串,等等)-不确定是否允许,我实际上很欢迎这样的实现,但我从未听说过现有的实现,或者对于给定的{errno,locale}对具有多个字符串。

动机

就上下文而言,我特别想要的(但我认为这个问题在更一般的方面很有价值,正如在评论中所讨论的那样)是能够在系统调用/函数writev中使用与errno相关的错误字符串。在我的特定用例中,我使用了argverrno链接字符串中的字符串。这将我的"最坏情况"长度设置为ARG_MAX+some max errno string length+size of a few other small strings)。

我查阅过的每一份*nix文档似乎都表明,如果iov_len值的总和溢出SSIZE_MAXwritev将(或"可能",在这种情况下,这种差异几乎没有好处)错误,errno设置为EINVAL。凭直觉,我知道我看到的每个errno字符串都很短,在实践中这不是问题。但是,如果这种假设可能是错误的,我不希望我的代码在某些系统上神秘地无法打印错误。所以我写了代码来处理这样的情况,但同时,我不希望为那些显然不需要的平台编译额外的代码

到目前为止,答案和评论的组合输入让我倾向于认为,在我的特定用例中,"正确"的解决方案是截断超长的消息——但这就是为什么我问这个问题,我最初是怎么做的:这些信息也有助于选择strerror_r/strerror_s(分别为*nix/Windows)的缓冲区大小,在我看来,即使是否定的回答(例如"你真的做不到")也对他人的教育有用。

相关

这个问题包含了VxWorks上strerror_r给出的字符串的答案,但我不愿意将其推广到所有系统。

您所构建的C库可能与您所运行的C库不同(可能使用ABI兼容的C库),甚至可能不同(在GNU/Linux上,请考虑glibc 2.2.5与glibc 2.23),因此,只能在进程执行期间的运行时计算从strerror返回的区域设置相关字符串的最大大小。除此之外,可以随时在目标系统上更新区域设置转换,这再次使该上限的任何预计算无效。

不幸的是,不能保证strerror返回的值在进程的生命周期内是恒定的,因此它们也可能在以后发生变化,从而使边界的任何早期计算无效。

我建议使用strerror_r来保存错误字符串,并避免非多线程感知库出现任何问题,这些库可能会调用sterror,并可能在复制字符串时更改字符串的结果。然后,您将使用保存的结果,而不是即时翻译字符串,并可能截断为SSIZE_MAX(这在现实中永远不会发生)。

我不知道C或C++标准对这些消息的长度做出了任何断言。不过,您感兴趣的平台可能会提供一些更强的实现定义保证。

例如,对于POSIX系统,我在limits.h中发现了以下内容。

以下常数应在<limits.h>中的所有实现中定义:

  • […]
  • {NL_TEXTMAX}
    消息字符串中的最大字节数
    最小可接受值:{_POSIX2_LINE_MAX}

我相信strerror产生的错误消息将属于这一类。

也就是说,我无法在我的系统上获得这个宏。但是,我确实有_POSIX2_LINE_MAX(来自<unistd.h>)。它是CCD_ 32 d到2048。由于标准只说这是一个下限,所以这可能没有太大帮助。

标准不保证strerror返回的以null结尾的字符串的大小限制。

在实践中,这永远不会成为一个问题。然而,如果您对此有点偏执,我建议您只需复制从strerr返回的字符串,并将其长度钳制为SSIZE_MAX,然后再将其传递给writev

可以放心地假设SSIZE_MAX将大于strerror在正常C或C++系统中返回的最长字符串(字符数组)。这是因为可用的系统内存(可由C程序直接使用)不能大于SIZE_MAX(一个无符号整数值),而SSIZE_MAX将至少具有相同的位数,因此使用2的互补数学来说明SSIZE_MAX(和SSIZE_t)的有符号性质SSIZE_MAX将至少是系统内存大小的1/2。