下面是一个例子:
#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
:
如果操作数的类型是可变长度数组类型,则计算操作数;否则,不计算操作数,结果为整数常量。
由于size_t
是unsigned
,标准保证unsigned
整数溢出具有定义良好的行为(不像signed
,其中可能会引发实现定义的信号)。
我感到困惑的主要问题是
size_t sz = sizeof(long[SIZE_MAX]); //error: size of unnamed array is too large
型
甚至不能编译Godbolt live example
4条答案
按热度按时间uqcuzwp81#
sizeof (long[SIZE_MAX])
无法编译,因为试图形成long[SIZE_MAX]
类型是违反约束的。来自C23标准草案的§6.2.5 28:完整类型的大小应小于或等于
SIZE_MAX
。有问题的约束没有列在 “约束” 标题下,所以编译器不需要 * 为此发出诊断。在这种情况下,GCC和Clang都选择失败并发出错误消息,但更一般地说,
sizeof (long[SIZE_MAX])
具有未定义的行为,因为它违反了显式约束子句之外的“shall”。当试图声明一个不受支持的数组时,实现将无法编译,并出现这样的错误。似乎这种语言没有出现在以前的标准中,但标准委员会确定"...that all interpret the current standard that huge objects make the behavior implicitly undefined."委员会认为这一变化不是引入了未定义的行为,而是澄清了这一点。
jpfvwuh42#
如果操作数的类型是可变长度数组类型,则计算操作数;否则,不计算操作数,结果为整数常量。
是的,它定义得很好。它在运行时为可变长度数组执行
sizeof(element)*number_of_elements
计算。结果很大也没关系。Barmar正确地提到:
虽然它可能定义得很好,但可能没有用。
vyswwuz23#
size_t
是一个类型,它必须足够大,以适应你实现中最大的对象的大小。所以它是定义良好的。但是如果你的对象的真实的计算大小(假设是无穷大的整数大小)>
SIZE_MAX
,那么它就不能在你的程序中创建或使用,那么它就完全没用了。kcrjzv8t4#
我感到困惑的主要问题是
甚至不能编译
SIZE_MAX
是一个 * 常量表达式 *,可以在翻译过程中计算;它是size_t
可以表示的最大值,因此编译器知道任何类型的char
以外的SIZE_MAX
元素数组将超过SIZE_MAX
字节并拒绝代码。它可以对任何值大于SIZE_MAX / sizeof (long)
的常量表达式执行此操作。cnt
* 不是 * 一个常量表达式,所以sizeof( long[cnt] )
直到 * 运行时 * 才被计算,编译器不能将其标记为问题。那么会发生什么呢?
6.2.5类型
...
9有符号整数类型的非负值范围是相应的无符号整数类型的子范围,并且每个类型中相同值的表示是相同的。41)* 涉及无符号操作数的计算永远不会溢出,因为不能由结果无符号整数类型表示的结果以大于可以表示的最大值的1的数为模进行归约结果类型**
所以,基本上,
size_t sz = sizeof( long[cnt] )
会给你给予某个值,这个值是对SIZE_MAX
取艾德的模。它不是数组的字节大小;它是某个可以放入size_t
的值。定义良好,但不是特别有用。