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

问题描述 投票:52回答:4

我和this answer的印象相同,size_t总是被标准保证足够大以容纳给定系统的最大可能类型。

但是,此代码无法在gcc / Mingw上编译:

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

typedef uint8_t array_t [SIZE_MAX];

错误:数组'array_t'的大小太大

我在这里误解了标准中的某些内容吗? size_t是否允许对于给定的实现来说太大?或者这是Mingw的另一个错误?


编辑:进一步的研究表明

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中的一个错误,因为根据有符号整数类型设置最大允许大小没有任何意义。

c gcc mingw size-t stdint
4个回答
60
投票

限制SIZE_MAX / 2来自您实现的size_t和ptrdiff_t的定义,它们选择类型ptrdiff_t和size_t具有相同的宽度。

C标准mandates1,类型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类型。

标准提供关于该主题的这3条评论4。


(引自ISO / IEC 9899:201x)

1(7.19通用定义2) 类型是 ptrdiff_t的 这是减去两个指针的结果的有符号整数类型; 为size_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,这意味着没有对象大小被视为运行时约束违规。


14
投票

范围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或更小的数组的两个指针之间的任何差异。
  2. 允许大小为SIZE_MAX的数组对象,并使用与ptrdiff_t宽度相同的size_t。如果指针比SIZE_MAX / 2元素更远,则接受指针减法可能溢出并导致未定义行为的事实。语言规范并未禁止此方法。
  3. 使用与ptrdiff_t宽度相同的size_t,并通过SIZE_MAX / 2限制最大数组对象大小。这样的ptrdiff_t可以容纳指向大小为SIZE_MAX / 2或更小的数组的两个指针之间的任何差异。

您只是在处理决定遵循第三种方法的实现。


5
投票

它看起来非常像特定于实现的行为。

我在这里运行Mac OS,并且使用gcc 6.3.0这个最大的大小我可以编译你的定义是SIZE_MAX/2;与SIZE_MAX/2 + 1它不再编译。

另一方面,女巫铿锵4.0.0最大的一个是SIZE_MAX/8SIZE_MAX/8 + 1休息。


0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.