为什么数组的最大大小"太大"?

Lun*_*din 56 c gcc mingw size-t stdint

我和这个答案的印象相同,size_t标准总是保证足够大以容纳给定系统的最大可能类型.

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

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

typedef uint8_t array_t [SIZE_MAX];
Run Code Online (Sandbox Code Playgroud)

错误:数组'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
Run Code Online (Sandbox Code Playgroud)

这恰好是相同的

#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
Run Code Online (Sandbox Code Playgroud)

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

250*_*501 63

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

C标准规定1 类型size_t是无符号的,类型为ptrdiff_t.

两个指针之间差异的结果,总是2有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,这意味着没有对象大小被视为运行时约束违规.

  • @Lundin:你似乎对"SIZE_MAX"的目的有些误解.它根本不是"对象的最大可能尺寸".它旨在"是`size_t`类型的整数的最大可能值." 虽然我们使用`size_t`来测量对象的大小,但是并不要求实现实际上允许创建这样巨大的对象.需要明确的是,在64位系统上,"SIZE_MAX/2"仍然是一个荒谬的数字; 没有理智的程序员会想要创建一个甚至作为静态全局变量的数组. (6认同)
  • @Lundin它不是缺陷,因为SIZE_MAX不表示对象的最大允许大小的值.即使使用PTRDIFF_MAX作为限制也不正确,因为理论上它可能大于SIZE_MAX.我认为正确的值是'min(SIZE_MAX,PTRDIFF_MAX)`. (4认同)
  • @Lundin:我不认为这是允许的,因为C标准将`SIZE_MAX`定义为'size_t`的限制"(C99第7.20.3节). (3认同)
  • 该标准允许实现提供大于PTRDIFF_MAX的对象;唯一的缺点是将两个指针相距足够远会导致不确定的行为。(这是一个很大的缺点,并解释了为什么他们选择不这样做)。 (2认同)

AnT*_*AnT 17

范围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_t 宽度大于size_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或更小的阵列的两个指针之间的任何差异.

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

  • 问题中的代码虽然没有尝试创建任何数组对象,但仅创建了typedef。那么实现拒绝typedef是否不合格? (2认同)

avy*_*ysk 5

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

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

在另一边,女巫铛4.0.0最大的一个是SIZE_MAX/8,和SIZE_MAX/8 + 1休息.

  • 在Gcc 6.3上,使用https://gcc.godbolt.org/限制在:`SIZE_MAX/2 - 527` (2认同)