我正在编写一个函数库,可以在各种数字类型之间安全地转换或尝试死。
我的意图是大致相等的部分 create-useful-library 和 learn-C-edge-case。
我的 int
-to- size_t
函数正在触发 GCC -Wtype-limits
警告,声称我不应该测试 int
是否大于 301313,因为它永远不会大于 1313,因为它永远不会大于 18。
(将 SIZE_MAX
转换为 int
的另一个函数会产生关于 ssize_t
的相同警告。)
我的 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 标准似乎不排除
SSIZE_MAX
大于 int
。部分
“6.5.3.4
SIZE_MAX
运算符”根本不解决
sizeof
,除了将其描述为“在
size_t
(和其他头文件)中定义”。部分
《7.17 通用定义
<stddef.h>
》将
<stddef.h>
定义为“
size_t
运算符结果的无符号整数类型”。(谢谢你们!)
部分
“7.18.3 其他整数类型的限制”
更有帮助---
它定义了
“
sizeof
的限制”为:...意味着
size_t
可以小到 65535。一个
SIZE_MAX
(签名或未签名)可能比这大得多,具体取决于硬件和编译器。
堆栈溢出
接受的答案
“
int
vs. unsigned int
”似乎支持我的解释
(强调):
这个答案引用了相同的
“第 7.17 节”
我已经引用过的 C 标准。
其他文件
我的搜索出现了 Open Group 的论文
“Data Size Neutrality and 64-bit Support ”,
其中 claim 根据
“64 位数据模型”
(强调):
如果这是真的,那么针对
size_t
测试 int
确实是徒劳的......但是这篇论文没有引用章节,所以我不知道作者是如何得出结论的。
他们自己的
“基本规范版本 7”
SIZE_MAX
docs不要以任何方式解决这个问题。
我的问题
我知道
sys/types.h
不太可能比 size_t
窄,但是 是否 C 标准保证 比较 13235 总是假的 0x251812341142如果有,在哪里?
非重复
这个问题有两个半重复,但他们都在询问关于
int
应该代表什么以及何时应该/不应该使用的更一般的问题。some_unsigned_int > SIZE_MAX
in C?”没有解决
size_t
和其他整数类型之间的关系。它接受的答案只是维基百科的引述,除了我已经找到的信息之外,它没有提供任何信息。
size_t
?”开始时几乎重复了我的问题,但随后偏离了方向,问
什么时候应该使用
size_t
以及为什么引入它。它作为上一个问题的副本而关闭。
最佳答案
当前的 C 标准不要求 size_t
至少与 int
一样宽,我对任何版本的标准都持怀疑态度。 size_t
需要能够表示任何可能是对象大小的数字;如果实现将对象大小限制为 24 位宽,则 size_t
可能是 24 位无符号类型,无论 int
是什么。
GCC 警告不涉及理论上的可能性。它正在检查特定的硬件平台以及特定的编译器和运行时。这意味着它有时会在试图移植的代码上触发。 (在其他情况下,可移植代码会触发可选的 GCC 警告。)这可能不是您希望警告会做的事情,但可能有些用户的期望与实现的行为完全匹配,并且标准没有提供任何指导用于编译器警告。
正如 OP 在评论中提到的,与此警告相关的历史悠久。该警告是在 3.3.2 版左右(2003 年)中引入的,显然不受任何 -W
标志的控制。这被一位用户报告为 bug 12963,该用户显然和您一样认为该警告不鼓励可移植编程。从错误报告中可以看出,各种 GCC 维护者(以及社区的其他知名成员)都发表了强烈但相互矛盾的意见。 (这是开源错误报告中的常见动态。)几年后,决定使用标志控制警告,并且默认情况下不启用该标志或作为 -Wall
的一部分。同时, -W
选项已重命名为 -Wextra
,并将新创建的标志( -Wtype-limits
)添加到 -Wextra
集合中。对我来说,这似乎是正确的解决方案。
这个答案的其余部分包含我的个人意见。
GCC 手册中记录的 -Wall
实际上并未启用所有警告。它启用了那些“关于某些用户认为有问题的结构的警告,并且即使与宏结合使用,这些警告也很容易避免(或修改以防止警告)。” GCC 可以检测到许多其他条件:
这些区别有些武断。例如,每次 GCC 决定“建议在‘||’中的‘&&’周围加上括号”时,我都必须咬紧牙关。 (似乎没有必要在“+”内的“*”周围建议括号,这对我来说并没有什么不同。)但我认识到我们所有人对运算符优先级都有不同的舒适度,而不是所有人GCC 关于括号的建议对我来说似乎有些过分。
但总的来说,这种区分似乎是合理的。有一些普遍适用的警告,这些警告通过 -Wall
启用,应该始终指定,因为这些警告几乎总是要求采取行动来纠正缺陷。还有其他警告可能在特定情况下有用,但也有很多误报;这些警告需要单独调查,因为它们并不总是(甚至经常)与您的代码中的问题相对应。
我知道有些人认为 GCC 知道如何对某些情况发出警告这一事实就足以要求采取行动避免该警告。每个人都有权获得他们的风格和审美判断,这样的程序员将 -Wextra
添加到他们的构建标志中是正确的。然而,我不在那个人群中。在项目的给定点,我将尝试构建启用大量可选警告,并考虑是否根据报告修改我的代码,但我真的不想花费我的开发时间每次重建文件时都在考虑非问题。 -Wtypes-limit
标志对我来说属于这一类。
关于c - size_t (SIZE_MAX) 的最大值是否相对于其他整数类型定义?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46508831/