size_t (SIZE_MAX) 的最大值是相对于其他整数类型定义的吗?

Is the max value of size_t (SIZE_MAX) defined relative to the other integer types?

我正在编写一个函数库,可以安全地在各种数字类型之间进行转换,否则就会死机。 我的意图是大致相等的部分 create-useful-library 和 learn-C-edge-cases。

我的 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 */
     ^

这些警告在我看来是不合理的,至少在一般情况下是这样。 (我不否认比较可能是 "always false" 在硬件和编译器的某些特定组合上。)

C 标准

C 1999 标准似乎不排除 int 大于 SIZE_MAX

小节 “6.5.3.4 sizeof 运算符” 根本没有解决 size_t,只是将其描述为 "defined in <stddef.h> (and other headers)".

小节 “7.17 通用定义 <stddef.h>” 将 size_t 定义为 "the unsigned integer type of the result of the sizeof operator"。 (谢谢,伙计们!)

小节 “7.18.3 其他整数类型的限制” 更有帮助--- 它定义 "limit of size_t" 作为:

SIZE_MAX 65535

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

堆栈溢出

已接受的答案 “unsigned int vs. size_t” 似乎支持我的解释 (强调):

The size_t type may be bigger than, equal to, or smaller than an unsigned int, and your compiler might make assumptions about it for optimization.

这个答案引用了相同的 "Section 7.17" 我已经引用过的 C 标准。

其他文件

我的搜索找到了 Open Group 的论文 "Data Size Neutrality and 64-bit Support", 根据哪个索赔 “64 位数据模型” (强调):

ISO/IEC 9899:1990, Programming Languages - C (ISO C) left the definition of the short int, the int, the long int, and the pointer deliberately vague [...] The only constraints were that ints must be no smaller than shorts, and longs must be no smaller than ints, and size_t must represent the largest unsigned type supported by an implementation. [...] The relationship between the fundamental data types can be expressed as:

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

如果这是真的,那么用 intSIZE_MAX 进行测试确实是徒劳的... 但是这篇论文没有引用章节和诗歌,所以我不知道它的作者是如何得出他们的结论的。 他们自己的 "Base Specification Version 7" sys/types.h docs 不要以任何方式解决这个问题。

我的问题

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

不重复

这个问题有两个半重复,但他们都在问更一般的问题,比如 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 集合中。对我来说,这似乎是正确的解决方案。


此回答的其余部分包含我的个人意见。

-Wall,如 GCC 手册中所述,实际上并未启用 all 警告。它启用这些警告 "about constructions that some users consider questionable, and that are easy to avoid (or modify to prevent the warning), even in conjunction with macros." GCC 可以检测到许多其他情况:

Note that some warning flags are not implied by -Wall. Some of them warn about constructions that users generally do not consider questionable, but which occasionally you might wish to check for; others warn about constructions that are necessary or hard to avoid in some cases, and there is no simple way to modify the code to suppress the warning. Some of them are enabled by -Wextra but many of them must be enabled individually.

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

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

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

我同意Remo.D's interpretation

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

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

不需要 size_t 的最大值大于 intSIZE_MAX <= INT_MAX 的架构很少见,我怀疑 GCC 会支持其中的 any

至于 fix,您可以使用 #if:

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