我正在编写一个函数库,可以在各种数字类型之间安全地转换或试晶。 我的意图大致相等地创建有用的库和学习C边缘的情况。
我的int
to-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_MAX
65535
。这意味着SIZE_MAX
可能小至 65535。 安int
(签名或未签名) 可能比这大得多,具体取决于硬件和编译器。
堆栈溢出
接受的答案 "unsigned int
vs.size_t
" 似乎支持我的解释 (着重号后加):
size_t
类型可能大于、等于或小于unsigned int
,编译器可能会对其进行假设以进行优化。
这个答案引用了相同的内容 "第 7.17 节" 我已经引用的 C 标准。
其他文件
我的搜索找到了开放小组的论文 "数据大小中立性和 64 位支持", 根据以下索赔 "64 位数据模型" (着重号后加):
ISO/IEC 9899:1990, 编程语言 - C (ISO C) 留下了
short int
、int
、long int
和pointer
的定义故意模糊 [...] 唯一的约束是int
s 必须不小于short
s,long
s 必须不小于int
s,并且size_t
必须表示实现支持的最大无符号类型。 [...] 基本数据类型之间的关系可以表示为:
sizeof(char)
<=sizeof(short)
<=sizeof(int)
<=sizeof(long)
=sizeof(size_t)
如果这是真的,那么测试针对SIZE_MAX
的int
确实是徒劳的...... 但是这篇论文没有引用章节,所以我不知道它的作者是如何得出结论的。 自己 "基本规范版本 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
大于int
。SIZE_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
。