c语言 - 相对于其他整数类型定义的 size_t (SIZE_MAX) 的最大值



我正在编写一个函数库,可以在各种数字类型之间安全地转换或试晶。 我的意图大致相等地创建有用的库和学习C边缘的情况。

我的intto-size_t函数触发了 GCC-Wtype-limits警告,声称我不应该测试int是否大于SIZE_MAX,因为它永远不会是真的。 (另一个将int转换为ssize_t的函数会生成有关SSIZE_MAX的相同警告。

我的MCVE,加上额外的评论和婴儿步骤,是:

#include <stdint.h>  /* SIZE_MAX */
#include <stdlib.h>  /* exit EXIT_FAILURE size_t */
extern size_t i2st(int value) {
if (value < 0) {
exit(EXIT_FAILURE);
}
// Safe to cast --- not too small.
unsigned int const u_value = (unsigned int) value;
if (u_value > SIZE_MAX) {  /* Line 10 */
exit(EXIT_FAILURE);
}
// Safe to cast --- not too big.
return (size_t) u_value;
}

编译器警告

我在 Linux 2.6.34 上收到了来自 GCC 4.4.5 的类似警告:

$ gcc -std=c99 -pedantic -Wall -Wextra -c -o math_utils.o math_utils.c
math_utils.c: In function ‘i2st’:
math_utils.c:10: warning: comparison is always false due to limited range of data type

。以及Linux 3.10.0上的GCC 4.8.5:

math_utils.c: In function ‘i2st’:
math_utils.c:10:5: warning: comparison is always false due to limited range of data type [-Wtype-limits]
if (u_value > SIZE_MAX) {  /* Line 10 */
^

这些警告对我来说似乎没有道理,至少在一般情况下是这样。 (我不否认比较可能是 "总是假的" 在硬件和编译器的某种特定组合上。

C 标准

C 1999 标准似乎不排除int大于SIZE_MAX的可能性。

部分 "6.5.3.4sizeof运算符" 根本不解决size_t,除了将其描述为 "在<stddef.h>(和其他标头)中定义"。

部分 "7.17 共同定义<stddef.h>size_t定义为 "sizeof运算符的结果的无符号整数类型"。 (谢谢,伙计们!

部分 "7.18.3 其他整数类型的限制" 更有帮助--- 它定义了 "size_t限制"为:

SIZE_MAX65535

。这意味着SIZE_MAX可能至 65535。 安int(签名或未签名) 可能比这大得多,具体取决于硬件和编译器。

堆栈溢出

接受的答案 "unsigned intvs.size_t" 似乎支持我的解释 (着重号后加):

size_t类型可能大于、等于或小于unsigned int,编译器可能会对其进行假设以进行优化。

这个答案引用了相同的内容 "第 7.17 节" 我已经引用的 C 标准。

其他文件

我的搜索找到了开放小组的论文 "数据大小中立性和 64 位支持", 根据以下索赔 "64 位数据模型" (着重号后加):

ISO/IEC 9899:1990, 编程语言 - C (ISO C) 留下了short intintlong intpointer的定义故意模糊 [...] 唯一的约束是ints 必须不小于shorts,longs 必须不小于ints,并且size_t必须表示实现支持的最大无符号类型。 [...] 基本数据类型之间的关系可以表示为:

sizeof(char)<=sizeof(short)<=sizeof(int)<=sizeof(long)=sizeof(size_t)

如果这是真的,那么测试针对SIZE_MAXint确实是徒劳的...... 但是这篇论文没有引用章节,所以我不知道它的作者是如何得出结论的。 自己 "基本规范版本 7"sys/types.h文档 无论哪种方式都不要解决这个问题。

我的问题

我知道size_t不太可能比int窄,但C标准是否保证比较some_unsigned_int > SIZE_MAX总是错误的? 如果是,在哪里?

不重复

这个问题有两个半重复,但它们都在问更一般的问题,关于size_t应该代表什么以及何时应该/不应该使用它。

  • "C中的size_t是什么?" 不解决size_t与其他整数类型之间的关系。 它接受的答案只是来自维基百科的引用,除了我已经找到的信息之外,它没有提供任何信息。

  • "size_t的正确定义是什么?" 开始几乎是我问题的重复,但随后偏离了方向,问 何时应该使用size_t以及为什么引入它。 它作为上一个问题的重复而关闭。

目前的C标准不要求size_t至少像int一样宽,我对任何版本的标准都持怀疑态度。size_t需要能够表示任何可能是对象大小的数字;如果实现将对象大小限制为 24 位宽,则无论int是什么,size_t都可以是 24 位无符号类型。

海湾合作委员会的警告没有提到理论上的可能性。它正在检查特定的硬件平台以及特定的编译器和运行时。这意味着它有时会在尝试可移植的代码上触发。(在其他情况下,可移植代码将触发可选的 GCC 警告。这可能不是您希望警告能够做到的,但可能有些用户的期望与实现的行为完全匹配,并且该标准没有为编译器警告提供任何指南。


正如OP在评论中提到的那样,与此警告相关的历史悠久。该警告是在 3.3.2 版左右(2003 年)中引入的,显然不受任何-W标志的控制。这被一个用户报告为错误 12963,他显然和你一样觉得警告不鼓励可移植编程。从错误报告中可以看出,各种GCC维护者(以及社区的其他知名成员)都提出了强烈但相互矛盾的意见。(这是开源错误报告中的常见动态。几年后,决定使用标志控制警告,并且默认情况下不启用该标志或作为-Wall的一部分。同时,-W选项被重命名为-Wextra,新创建的标志(-Wtype-limits)被添加到-Wextra集合中。对我来说,这似乎是正确的解决方案。


这个答案的其余部分包含我个人的看法。

-Wall,如 GCC 手册中所述,实际上并未启用所有警告。它使那些警告"关于一些用户认为有问题的构造,并且很容易避免(或修改以防止警告),即使与宏结合使用也是如此。GCC 还可以检测到许多其他情况:

请注意,-Wall不暗示某些警告标志。其中一些警告用户通常不认为有问题但偶尔您可能希望检查的结构;其他人警告在某些情况下必要或难以避免的构造,并且没有简单的方法来修改代码来抑制警告。其中一些是由-Wextra启用的,但其中许多必须单独启用。

这些区别有些武断。例如,每次 GCC 决定"建议在'||'中的'&&'周围加上括号"时,我都必须咬紧牙关。(似乎没有必要在"+"中的"*"周围建议括号,这对我来说没有什么不同。但我认识到,我们所有人都对运算符优先级有不同的舒适度,而且并非所有 GCC 关于括号的建议对我来说都过分了。

但总的来说,这种区别似乎是合理的。有一些警告是普遍适用的,并且这些警告是用-Wall启用的,应始终指定这些警告,因为这些警告几乎总是要求采取措施来纠正缺陷。还有其他警告在特定情况下可能有用,但也有很多误报;这些警告需要单独调查,因为它们并不总是(甚至经常)与代码中的问题相对应。

我知道有些人认为,海湾合作委员会知道如何警告某些情况这一事实就足以要求采取行动以避免这种警告。每个人都有权拥有自己的风格和审美判断,这样的程序员在他们的构建标志上添加-Wextra是正确和公正的。然而,我不在那群人中。在项目中的某个给定点,我将尝试启用大量可选警告的构建,并考虑是否根据报告修改我的代码,但我真的不想在每次重建文件时都花费开发时间考虑非问题。-Wtypes-limit旗对我来说属于这一类。

没有什么要求最大size_t大于intSIZE_MAX<=INT_MAX的这种架构很少见,我怀疑 GCC 会支持其中任何一个

至于修复,您可以使用#if

#if INT_MAX > SIZE_MAX
if (u_value > SIZE_MAX) {  /* Line 10 */
exit(EXIT_FAILURE);
}
#endif

<</div> div class="one_answers">我同意Remo.D的解释。

size_t被指定为标准的无符号整数,但该标准不限制其相对于其中任何一个的大小,除了说它必须能够容纳至少65535.

因此,它可以小于、等于或大于unsigned int

最新更新