gcc 为什么数组的最大大小“太大”?

dy2hfwbg  于 2022-11-12  发布在  其他
关注(0)|答案(4)|浏览(199)

我的印象和this answer一样,size_t总是由标准保证足够大,以容纳给定系统的最大可能类型。
但是,以下代码无法在gcc/Mingw上编译:

#include <stdint.h>
#include <stddef.h>

typedef uint8_t array_t [SIZE_MAX];

错误:数组'array_t'的大小太大
我是不是误解了标准中的一些东西?size_t对于一个给定的实现来说是否允许太大?或者这是Mingw中的另一个bug?
编辑:进一步的研究表明

typedef uint8_t array_t [SIZE_MAX/2];   // does compile
typedef uint8_t array_t [SIZE_MAX/2+1]; // does not compile

这恰好和

#include <limits.h>

typedef uint8_t array_t [LLONG_MAX];           // does compile
typedef uint8_t array_t [LLONG_MAX+(size_t)1]; // does not compile

所以我现在倾向于认为这是Mingw中的一个bug,因为基于有符号整数类型设置最大允许大小没有任何意义。

qgzx9mmu

qgzx9mmu1#

限制SIZE_MAX / 2来自您的实作上size_t和ptrdiff_t的定义,这两个定义会选择类型ptrdiff_t和size_t具有相同长度。
C标准要求1 size_t类型是无符号的,而ptrdiff_t类型是有符号的。
两个指针之间的差的结果将始终为ptrdiff_t类型。这意味着,在您的实现中,对象的大小必须限制为PTRDIFF_MAX,否则两个指针的有效差无法以ptrdiff_t类型表示,从而导致未定义的行为。
因此,值SIZE_MAX / 2等于值PTRDIFF_MAX。如果实现选择使最大对象大小为SIZE_MAX,则必须增加类型ptrdiff_t的宽度。但是,将对象的最大大小限制为SIZE_MAX / 2要容易得多,然后使类型ptrdiff_t具有比类型size_t更大或相等的正范围。
Standard提供了有关该主题的以下3评论4。
(引自ISO/IEC 9899:201 x)
1(7.19通用定义2)
类型包括
指针差异_t
为两个指针相减后的结果的有符号整数类型;
大小_t
它是sizeof运算符结果的无符号整数类型;
2(6.5.6加法运算符9)
当两个指针相减时,两个指针都应指向同一数组对象的元素,或者一个指针指向数组对象的最后一个元素之后的元素;结果是两个数组元素的下标之差。结果的大小是由实现定义的,其类型(有符号整数类型)是标头中定义的ptrdiff_t。如果结果不能在该类型的对象中表示,则行为是未定义的。
3(K.3.4整数类型3)
极大的对象大小通常是对象大小计算不正确的标志。例如,负数在转换为size_t这样的无符号类型时,会显示为非常大的正数。此外,某些实现不支持与size_t类型所能表示的最大值一样大的对象。
4(K.3.4整数类型4)
由于这些原因,有时限制对象大小的范围以检测编程错误是有益的。对于以具有大地址空间的计算机为目标的实现,建议将RSIZE_MAX定义为所支持的最大对象的大小或(SIZE_MAX〉〉1),即使这个限制小于一些合法的大小,但是非常大,以具有小地址空间的机器为目标的实现可能希望将RSIZE_MAX定义为SIZE_MAX,这意味着不存在被认为违反运行时约束的对象大小。

lf5gs5x2

lf5gs5x22#

范围为size_t的保证足以储存实作所支援的最大对象大小。反之则不成立:您不能保证创建的对象的大小能够填充size_t的整个范围。
在这种情况下,问题是:SIZE_MAX代表什么?支持的最大对象大小?还是size_t中可表示的最大值?答案是:它是后者,即SIZE_MAX(size_t) -1。不能保证您能够创建SIZE_MAX字节大小的对象。
原因是除了size_t之外,实现还必须提供ptrdiff_t,它旨在(但不保证)存储指向同一数组对象的两个指针之间的差异。由于类型ptrdiff_t是带符号的,实现面临以下选择:
1.允许数组对象大小为SIZE_MAX,并使ptrdiff_tsize_t * 宽 *。它必须至少宽一位。这样的ptrdiff_t可以容纳指向大小为SIZE_MAX或更小的数组的两个指针之间的任何差异。
1.允许大小为SIZE_MAX的数组对象,并使用与size_t * 宽度相同 * 的ptrdiff_t。如果指针之间的距离大于SIZE_MAX / 2元素,则接受指针减法可能 * 溢出 * 并导致未定义行为的事实。语言规范不禁止此方法。
1.使用与size_t宽度相同的ptrdiff_t,并 * 限制 * 最大数组对象大小为SIZE_MAX / 2。这样的ptrdiff_t可以容纳指向大小为SIZE_MAX / 2或更小的数组的两个指针之间的任何差异。
您只是在处理一个决定遵循第三种方法的实现。

xpszyzbs

xpszyzbs3#

它看起来非常像特定于实现的行为。
我在这里运行的是MacOS,在gcc6.3.0中,我可以编译定义的最大大小是SIZE_MAX/2;对于SIZE_MAX/2 + 1,它不再编译。
在另一边,witch clang 4.0.0最大的一个是SIZE_MAX/8SIZE_MAX/8 + 1中断。

uurv41yg

uurv41yg4#

从头开始推理,size_t是一种可以容纳任何对象大小的类型。(忽略多路复用和可以处理例如32和64位代码的系统,称之为“代码宽度”)。类似于最大整数值MAX_INTSIZE_MAXsize_t。因此,一个SIZE_MAX大小的对象都是可寻址内存。一个实现将其标记为错误是合理的,但是,我同意只有在实际对象被分配的情况下才是错误的,无论它是在堆栈上还是在全局内存中。(对malloc的调用无论如何都会失败)

相关问题