【问题标题】:C/C++: maximum size of errno-associated strings (at compile-time)C/C++:与 errno 相关的字符串的最大大小(在编译时)
【发布时间】:2018-11-16 07:41:54
【问题描述】:

问题

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

我的想法

我能想到的最好的方法是运行一个程序,在 int 的范围内,在每个语言环境上进行暴力搜索,以获取与每个 {errno, locale} 对关联的字符串,获取它的大小,然后在该系统上生成一个标头,然后将其挂接到例如一个makefile或autoconf或其他什么。我想不出更好的方法来做到这一点,但这样做似乎很荒谬:系统的标准库具有内置的信息,即使只是隐式的。真的没有很好的方法来获取这些信息吗?

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

动机

对于上下文,我特别想要的是能够使用与 errno 关联的错误字符串(但我认为这个问题在更普遍的方式中很有价值,正如 cmets 中所讨论的那样)导致这个问题在系统调用/函数writev 中。在我的特定用例中,我使用了argv errno-linked 字符串中的字符串。这将我的“最坏情况”长度设置为ARG_MAX + some max errno string length + size of a few other small strings)。

我查阅过的每个 *nix 文档似乎都表明 writev 将(或“可能”,在这种情况下这种差异产生的一点好处)错误,如果将 errno 设置为 EINVAL iov_len 值溢出 SSIZE_MAX。直觉上,我知道我见过的每个errno 字符串都很短,实际上这不是问题。但是如果这个假设可能是错误的,我不希望我的代码在某些系统上神秘地无法打印错误。所以我编写了代码来处理这种情况 - 但同时,我不希望为通常显然不需要它的平台编译额外的代码。

到目前为止,答案和 cmets 的组合输入使我倾向于认为在我的特定用例中,“正确”的解决方案是截断非常长的消息 - 但这就是为什么我问这个问题我是如何最初是这样做的:这些信息还有助于为strerror_r/strerror_s(分别为*nix/Windows)选择一个缓冲区的大小,甚至一个否定的答案(例如“你不能真的这样做”)在我的认为对他人的教育有用。

相关

This question 包含对 VxWorks 上strerror_r 给出的字符串的答案,但我不方便将其推广到所有系统。

【问题讨论】:

  • C 和 C++ 是不同的语言!
  • @Olaf 虽然我总体上感谢您为明确这一区别所做的努力,但我相信在这种情况下,将 C 和 C++ 放在一起是合法的,因为它们都使用了 errno 概念.选择其中一个标签而不是另一个标签是任意的。
  • 您可以选择自己的最大值,并在运行时剪辑错误消息吗?
  • 我可能遗漏了一些东西,但是在 ssize_t 大于 int8_t 的任何平台上,大于 ssize_t 的错误消息似乎完全是疯狂的。
  • C(我怀疑是 C++)标准对SSIZE_MAX 没有帮助,因为它没有定义SSIZE_MAX。如果SSIZE_MAX 对您的代码很重要,建议标记定义它的感兴趣的环境。

标签: c++ c


【解决方案1】:

您构建的 C 库可能与您运行的 C 库的版本不同(可能使用与 ABI 兼容的 C 库)甚至是完全相同的 C 库版本(在 GNU/Linux 上考虑 glibc 2.2.5 与 glibc 2.23) ,因此计算从strerror 返回的与语言环境相关的字符串的最大大小只能在进程执行期间的运行时完成。除此之外,可以随时在目标系统上更新语言环境翻译,这再次使该上限的任何预计算无效。

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

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

【讨论】:

  • +1,但请注意,具有上限对于选择传递给 strerror_r 的缓冲区的大小也非常有用,从而减少了对运行时逻辑的需求。此外,我已经考虑并意识到 maxima-could-change-out-from-under-your-program 情况 - 但我希望有一些标准限制,或者至少我们可以检查特定于平台的宏至少在某些系统上,这对于至少某些系统来说可能不是问题,因此是个问题。
  • 特别值得注意的是,如果给它的缓冲区不够大,POSIX(根据您的链接)允许strerror_rERANGE 失败:我的阅读表明一个实现首先检查长度,并且不写任何内容就出错,这将是符合标准的。我想相信没有实现这样做,或者我误读了,但如果是这样,那么似乎不知道上限,似乎我们必须在适度慷慨的缓冲区一侧犯错来保存它,或者循环重新分配直到它返回成功?
  • 无法保证 strerrorbuf 已被返回 ERANGEstrerror_r 调用修改。因此,您必须准备好处理标准规范文本中指定的各种回报组合。
  • 经过几天的沉思,我(暂时)接受了这个答案,因为它既 1)一般性又 2)提供了关于为什么此类信息不能(也许应该不)在许多实现的编译时可用。
【解决方案2】:

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

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

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

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

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

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

【讨论】:

  • 我能够通过定义_GNU_SOURCE 来定义NL_TEXTMAX。因此,我怀疑为_POSIX_C_SOURCE 定义的足够“高”的值会将其包含在内。无论如何,值是 0x7fffffff 或 2147483647。它本身并不太鼓舞人心 - 尽管 SSIZE_MAX 的宽度是原来的两倍(f 后面还有 8 个数字)。
  • 作为记录,我也对此进行了 +1,我很欣赏针对我的特定用例提供的信息。尽管我目前接受了另一个答案,但我认为这是下一个最佳答案,因为它涵盖了主要答案中未提供的信息,并且确实建议(尽管不确定)某些实现或其他标准建立在语言规范之上,实际上可能定义了某种上限。
【解决方案3】:

The standards make no guarantees 关于strerror 返回的以空字符结尾的字符串的大小限制。

在实践中,这永远不会成为问题。但是,如果您对此持怀疑态度,我建议您只需复制从strerr 返回的字符串,并将其长度限制为SSIZE_MAX,然后再将其传递给writev

【讨论】:

  • 我倾向于同意该标准在这方面不做任何保证,但我不认为 cppreference.com 是这方面的权威参考。
【解决方案4】:

可以安全地假设SSIZE_MAX 将大于 strerror 在普通 C 或 C++ 系统中返回的最长字符串(字符数组)。这是因为可用的系统内存(可由您的 C 程序直接使用)不能大于 SIZE_MAX(一个无符号整数值)并且 SSIZE_MAX 将至少具有相同数量的位,因此使用 2 的补码数学来解释SSIZE_MAX(和 ssize_t)的有符号性质 SSIZE_MAX 将至少是系统内存大小的 1/2。

【讨论】:

  • 详细信息:“这是因为可用系统内存(C 程序可以直接使用)不能大于 SIZE_MAX” --> 不同意,SIZE_MAX 是数组索引的最大值single 数组,不是最大内存量。关于SSIZE_MAX:C 中没有定义,所以最好引用你的参考资料。
  • SIZE_MAX 也是 C2011 struct 到但不包括最后一个成员的部分的最大大小,因为它是标准 offsetof() 宏的结果类型。
  • @chux:标准 C 有一个平面 (von Neumann) 内存模型,所有这些内存都可以被视为单个数组 (((char *)0)[x])。哈佛(如大多数 AVR 和 8051 MCU)、分段(如 80286)或其他系统的 C 编译器通常使用非标准语言扩展来实现对不同内存区域的访问或对语言功能引入限制以避免一些内存模型问题. stackoverflow.com/questions/8649018/… 的回答不同意,但使用这些模仿写将受到严重限制。
  • Post C 立场参考支持这一点。根据我的阅读,没有,所以不同意不受支持的“标准 C 具有平坦(冯诺依曼)内存模型,所有这些内存都可以被视为单个数组(((char *)0)[x])”并且该模型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-10-18
  • 1970-01-01
  • 1970-01-01
  • 2022-12-17
  • 1970-01-01
  • 2019-05-09
  • 2021-05-23
相关资源
最近更新 更多