为什么数组的最大大小“太大”?
我和这个答案的印象一样, size_t
总是由标准保证足够大,以保持给定系统的最大可能types。
但是,这段代码无法在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,因为基于有符号整数types设置允许的最大大小没有任何意义。
限制SIZE_MAX / 2来自您的实现上的size_t和ptrdiff_t的定义,它们selecttypesptrdiff_t和size_t具有相同的宽度。
C标准要求1 ,typessize_t是无符号的,typesptrdiff_t是有符号的。
两个指针之间的差异的结果将始终是2的typesptrdiff_t。 这意味着,在你的实现中,对象的大小必须限制在PTRDIFF_MAX,否则两个指针的有效差别不能用ptrdiff_ttypes表示,导致未定义的行为。
因此,值SIZE_MAX / 2等于值PTRDIFF_MAX。 如果实现select使最大对象大小为SIZE_MAX,则typesptrdiff_t的宽度将不得不增加。 但是,将对象的最大尺寸限制为SIZE_MAX / 2要容易得多,那么typesptrdiff_t要比typessize_t有更大或相等的正范围。
标准提供这3个评论4关于这个话题。
(引自ISO / IEC 9899:201x)
1 (7.19一般定义2)
types是
ptrdiff_t的
这是减去两个指针的结果的有符号整数types;
为size_t
这是sizeof运算符的结果的无符号整数types;
2 (6.5.6添加操作符9)
当减去两个指针时,都应指向相同数组对象的元素,或指向数组对象的最后一个元素之后的元素; 结果是两个数组元素的下标差异。 结果的大小是实现定义的,它的types(一个有符号整数types)是在头文件中定义的ptrdiff_t。 如果结果在该types的对象中不可表示,则行为是未定义的。
3 (K.3.4整型3)
非常大的对象大小通常表示对象大小计算错误。 例如,当转换为像size_t这样的无符号types时,负数显示为非常大的正数。 另外,有些实现不支持与size_ttypes可以表示的最大值一样大的对象。
4 (K.3.4整型4)
由于这些原因,限制对象大小的范围以检测编程错误有时是有益的。 对于具有大地址空间的机器的实现,build议将RSIZE_MAX定义为所支持的最大对象的大小或(SIZE_MAX >> 1)中的较小者,即使此限制小于某些合法的大小,但非常小大,对象。 针对具有小地址空间的机器的实现可能希望将RSIZE_MAX定义为SIZE_MAX,这意味着没有被视为运行时约束违规的对象大小。
范围size_t
保证足以存储实现所支持的最大对象的大小。 反之亦然:你不能保证能够创build一个大小满足size_t
的整个范围的对象。
在这种情况下,问题是: SIZE_MAX
代表什么? 最大的支持对象大小? 或size_t
表示的最大值? 答案是:后者是SIZE_MAX
(size_t) -1
。 您不能保证能够创build大SIZE_MAX
字节的对象。
其原因是,除了size_t
,实现还必须提供ptrdiff_t
,它旨在(但不能保证)存储指向同一数组对象的两个指针之间的差异。 由于typesptrdiff_t
是有符号的,所以实现面临以下select:
-
允许大小为
SIZE_MAX
数组对象,并使ptrdiff_t
大于size_t
。 它至less要扩大一点。 这样的ptrdiff_t
可以容纳指向一个SIZE_MAX
或更小的数组的两个指针之间的差异。 -
允许大小为
SIZE_MAX
数组对象,并使用与size_t
具有相同宽度的ptrdiff_t
。 如果指针比SIZE_MAX / 2
元素分开的更远,则接受指针减法溢出并导致未定义行为的事实。 语言规范并不禁止这种方法。 -
使用与
size_t
相同宽度的ptrdiff_t
,并通过SIZE_MAX / 2
限制最大数组对象大小。 这样的ptrdiff_t
可以容纳指向SIZE_MAX / 2
或更小的数组的两个指针之间的差异。
你只是在处理一个决定采用第三种方法的实现。
它看起来非常像实现特定的行为。
我在这里运行的Mac OS,并与海湾合作委员会6.3.0最大的尺寸,我可以编译你的定义是SIZE_MAX/2
; 与SIZE_MAX/2 + 1
它不能编译了。
另一方面,女巫cla 4.0.0最大的是SIZE_MAX/8
, SIZE_MAX/8 + 1
中断。
只是从头开始推理, size_t
是一个可以容纳任何对象的大小的types。 任何对象的大小都受地址总线宽度的限制(忽略多路复用和可处理例如32位和64位代码的系统,称之为“代码宽度”)。 MAX_INT
是最大的整数值, SIZE_MAX
是size_t
的最大值。 因此,大小为SIZE_MAX
的对象都是可寻址的存储器。 然而,一个实现标记为错误是合理的,但是,我同意,只有在实际的对象被分配的情况下,这个错误才会出现在堆栈或全局内存中。 (对malloc
的调用将会失败)
首先, size_t
用于保存sizeof
运算符的结果。 所以,保证有一个大小可以保持SIZE_MAX
的“价值”。 从理论上讲,应该允许你定义任何大小为SIZE_MAX
对象。
那么,如果我没有记错的话,你是受到全系统资源上限的限制的。 这不是C标准强加的,而是OS /环境强加的。
检查ulimit -a
输出。 您也可以使用ulimit -s <size>
修改堆栈的限制,只要您定义的数组存储在堆栈中即可 。 否则,对于全局数组,可能需要根据操作系统检查.DATA或.BSS中允许的大小。 所以,这是依赖于环境(或实现相关)。