C/C++:errno关联字符串的最大大小(编译时)
C/C++: maximum size of errno-associated strings (at compile-time)
问题
有没有办法在编译时(在预处理器时会更好)获得与errno
相关的任何字符串的最大大小?例如strlen(strerror(errno))
的上界?
我的想法
我能想到的最好的方法是运行一个程序,在int的范围内,在每个区域设置上进行强力搜索,获取与每个{errno,locale}对相关的字符串,获取其大小,并在该系统上生成一个标头,然后将其挂接到例如makefile或autoconf或其他文件中。我想不出更好的方法来做到这一点,但这样做似乎很荒谬:系统的标准库内置了这些信息,哪怕只是隐含的。真的没有好的方法来获取这些信息吗?
好吧,我承认C和/或C++标准可能允许在运行时生成错误字符串,例如特定于环境的消息(例如strerror(EINVAL)
给出了上次设置errno
时从其他运行时元数据集派生的字符串,等等)-不确定是否允许,我实际上很欢迎这样的实现,但我从未听说过现有的实现,或者对于给定的{errno
,locale}对具有多个字符串。
动机
就上下文而言,我特别想要的(但我认为这个问题在更一般的方面很有价值,正如在评论中所讨论的那样)是能够在系统调用/函数writev
中使用与errno
相关的错误字符串。在我的特定用例中,我使用了argv
和errno
链接字符串中的字符串。这将我的"最坏情况"长度设置为ARG_MAX
+some max errno string length
+size of a few other small strings
)。
我查阅过的每一份*nix文档似乎都表明,如果iov_len
值的总和溢出SSIZE_MAX
,writev
将(或"可能",在这种情况下,这种差异几乎没有好处)错误,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。