计算长数组SIZE_MAX元素的大小

Evaluating sizeof long array of SIZE_MAX elements

提问人:Some Name 提问时间:10/28/2023 最后编辑:Some Name 更新时间:10/28/2023 访问量:191

问:

请考虑以下代码:

#include <stdio.h>
#include <limits.h>
#include <inttypes.h>
#include <stddef.h>

int main(){
    size_t cnt = SIZE_MAX;
    size_t sz = sizeof(long[cnt]);
    printf("%zu\n", sz);
}

6.5.3.4/p2:

如果操作数的类型是可变长度数组类型,则 对操作数进行评估;否则,不会计算操作数,并且 result 是一个整数常量。

问题是,这种太大的评估是否定义得当?由于是,该标准保证整数溢出具有明确定义的行为(与可能引发实现定义信号的情况不同)。sizeofsize_tunsignedunsignedsigned

我感到困惑的主要问题是

size_t sz = sizeof(long[SIZE_MAX]); //error: size of unnamed array is too large

甚至不编译 Godbolt 实时示例

c sizeof

评论

1赞 KamilCuk 10/28/2023
有,可能更能说明这一点。SIZE_MAX
2赞 Barmar 10/28/2023
long[cnt]可能大于允许的数组类型。
0赞 Some Name 10/28/2023
@Barmar不太明白重点......无论如何,我们都会溢出
0赞 Some Name 10/28/2023
@KamilCuk 好点,固定。

答:

4赞 Ted Lyngmo 10/28/2023 #1

如果操作数的类型是可变长度数组类型,则计算操作数;否则,不会计算操作数,并且结果为整数常量。

是的,它定义明确。它在运行时对可变长度数组执行计算。结果大也没关系。sizeof(element)*number_of_elements

Barmar 正确地提到:

虽然它可能定义得很好,但它可能没有用。

评论

0赞 Barmar 10/28/2023
但是它很有可能会溢出(例如,如果)。因此,虽然它可能定义得很好,但它可能没有用。size_t == unsigned long
0赞 Ted Lyngmo 10/28/2023
@Barmar 好点子。我添加了你的笔记!
2赞 Ted Lyngmo 10/28/2023
@SomeName 不过,这不是一个可变长度的数组。
1赞 Ted Lyngmo 10/28/2023
@SomeName不确定。我需要在标准中检查这一点。我使用的所有编译器都拒绝编译它,因为它太大了,但我不确定这是否是标准所规定的。
1赞 Some Name 10/28/2023
@TedLyngmo 我唯一能看到的一点是:两个运算符的结果值都是实现定义的,因此编译错误可以被视为实现定义的行为6.5.3.4/p5
3赞 0___________ 10/28/2023 #2

size_t是必须足够大以容纳实现中最大可能对象的大小的类型。所以它是明确的。

但是,如果对象的实际计算大小(假设为不定式整数大小)>则无法在程序中创建或使用该对象。那么它就完全没用了。SIZE_MAX

评论

0赞 Some Name 10/28/2023
反正都会有溢出,但为什么编译器不允许呢?它因编译错误而被拒绝......sizeof(long[SIZE_MAX])
0赞 0___________ 10/28/2023
因为这个是在编译时完成的
0赞 Some Name 10/28/2023
不清楚为什么它不是溢出。如果数组大小计算等同于,那么就会有溢出...element_size * element_count
2赞 0___________ 10/28/2023
@SomeName无符号整数不会溢出,只会环绕。这是定义良好的行为,有符号整数溢出 - 不是。这就是区别。
0赞 Eric Postpischil 10/28/2023
Re “是必须足够大以容纳实现中最大可能对象大小的类型”:当前的 C 标准 C 2018 并没有这么说。size_t
5赞 ad absurdum 10/28/2023 #3

sizeof (long[SIZE_MAX])不会编译,因为尝试形成类型是违反约束的。从 C6.2.5 标准草案的 §28 23 开始:long[SIZE_MAX]

完整类型的大小应小于或等于 。SIZE_MAX

相关约束未列在“约束”标题下,因此编译器不需要为此发出诊断。在这种情况下,GCC 和 Clang 都选择失败并发出错误消息,但更常见的是具有未定义的行为,因为它违反了显式约束子句之外的“shall”。但我认为,当尝试声明一个不支持的数组时,合理的实现将无法编译并出现这样的错误。sizeof (long[SIZE_MAX])

这种语言似乎没有出现在以前的标准中,但标准委员会确定“......所有这些都解释了当前的标准,即巨大的物体使行为隐含地未定义。委员会认为,这一变化不是引入一种未定义的行为,而是明确这一点的澄清。

评论

2赞 Ted Lyngmo 10/28/2023
这很好!我想知道拒绝编译是否是实现定义的。这当然是“......或等于 SIZE_MAX”。sizeof(char[SIZE_MAX])
1赞 Ted Lyngmo 10/28/2023
@adabsurdum 或者它可能被开放以进行实现定义。我读到的 bugzilla 问题(gcc.gnu.org/bugzilla/show_bug.cgi?id=70588)提到设置了一个比那里低得多的限制,但目前,它可以编译,而没有。SIZE_MAX/2SIZE_MAX/2+1
3赞 Eric Postpischil 10/28/2023
Re “won't compile”:违反了 6.2.5 28 中的约束,但这不在约束段落中,因此该标准不要求编译器为其发出诊断或不编译它。如果它无法编译,则是一种实现选择。sizeof (long [SIZE_MAX])
2赞 Ted Lyngmo 10/28/2023
@EricPostpischil 因此,最终取决于实施者。它不会通过设置任意上限(只要遵守任何最小限制)来使实现不合规。对我有用。
1赞 ad absurdum 10/28/2023
@SomeName“......在C23之前定义明确......“——标准委员会认为这是一种澄清,而不是引入新的未定义行为。他们似乎发现流行的解释是“巨大的物体使行为隐含地未定义”。 6.2.5 现在明确了这一点。
3赞 John Bode 10/28/2023 #4
我感到困惑的主要问题是它甚至没有编译
size_t sz = sizeof(long[SIZE_MAX]); //error: size of unnamed array is too large

SIZE_MAX是一个常量表达式,可以在翻译过程中进行计算;它是可以用 表示的最大值,因此编译器知道除 以外的任何类型的元素数组将超过字节并拒绝代码。它可以对值大于 的任何常量表达式执行此操作。size_tSIZE_MAXcharSIZE_MAXSIZE_MAX / sizeof (long)

cnt不是常量表达式,因此在运行时之前不会进行评估,并且编译器无法将其标记为问题。sizeof( long[cnt] )

那么会发生什么呢?

6.2.5 类型

...

9 有符号整数类型的非负值范围是相应无符号的子范围 整数类型,并且每种类型中相同值的表示形式相同。41) 计算 涉及无符号操作数永远不会溢出,因为无法用 生成的无符号整数类型是缩小模数,该数比最大数大 1 可以由结果类型表示的值。

所以,基本上,会给你一些由修改的价值。它不会是数组的大小(以字节为单位);它将是一些可以放入 .定义明确,但不是非常有用。size_t sz = sizeof( long[cnt] )SIZE_MAXsize_t