【问题标题】:Array index greater than PTRDIFF_MAX数组索引大于 PTRDIFF_MAX
【发布时间】:2020-11-14 11:55:45
【问题描述】:

让我们有一个char 数组:

char arr[(size_t)PTRDIFF_MAX + (size_t)2];

让我们接受我们的系统有足够的可用内存。

以数组表示法或指针表示法访问数组是已定义、未定义或实现定义的行为?

char c = arr[(size_t)PTRDIFF_MAX + (size_t)1];
char d = *(&arr[0] + ((size_t)PTRDIFF_MAX + (size_t)1));

我担心索引可能会在访问之前转换为ptrdiff_t,从而导致索引无效。


编辑:

在不可能有​​这么多元素的数组的情况下,让我们把这个类似的情况:

我们有一个比PTRDIFF_MAX 少元素的数组,但是大元素(例如:int64_t,或具有许多元素的结构),因此数组的原始大小仍然超过PTRDIFF_MAX。我们通过char * 访问数组,这是一个有效的强制转换(或多或少在memcpy 内部发生了什么)。

【问题讨论】:

  • 这看起来很相关:stackoverflow.com/a/31864574/634919。我认为简短的回答是您根本无法拥有那么大的数组。
  • 我假设这是在一台 32 位机器上,PTRDIFF_MAX0x7FFFFFFF(31 位)?在 64 位机器上,PTRDIFF_MAX0x7FFFFFFFFFFFFFFF,所以你不太可能靠近 [或关心]。但是,这是一个 signed 数量。 PTRDIFF_MIN0x80000000 它可以取决于环境。您可以解决这个问题。例如,在 32 位机器上,可寻址性为 0xFFFFFFFF(32 位)。签名是因为您想在这样做时倒退:&arr[3] - &arr[5]。但是,即使未签名也有效。指针算术是无符号的。所以,PTRDIFF_MAX 可能不是看待它的最佳方式
  • @CraigEstey re:32 位或 64:是的,32 或更少。 re: 指针算法是无符号的: 很高兴知道。我认为因为指针算法使用了ptrdiff_t,所以它是签名的。这在标准中的什么地方?
  • @CacahueteFrito:标准在哪里说指针算术使用ptrdiff_t?唯一的说法是指针减法(不是计算)的结果作为ptrdiff_t返回。
  • 它明确表示i-j 必须适合ptrdiff_t,而不是地址之间的差异。我会在我的答案中添加语言律师的报价。

标签: arrays c types


【解决方案1】:

数组索引未转换为ptrdiff_t

将整数添加到指针是使用整数加数的实际值(包括符号)完成的,因此数组索引不是问题。 (当然,您必须确保您提供的索引类型足够宽,并且该值在合法索引值的范围内。但如果是这种情况,编译器不会更改类型。)

当您减去指针时,可能会出现问题;在这种情况下,如果减法的结果不能表示为ptrdiff_t,则结果是未定义的。但请记住,结果是数组元素的数量,而不是字节数。当然,如果是char的数组,则没有区别,但是对于大小大于1的数组元素类型,假设ptrdiff_tsize_t的有符号版本是不可能溢出的。

标准对指针减法的结果是明确的:

如果表达式 PQ 分别指向 i-th 和 j-th数组对象的元素,表达式 (P)-(Q) 具有值 i-j,前提是该值适合 ptrdiff_t 类型的对象。 (§6.5.6p9)。

该措辞特意强调它是数组索引之间的差异,而不是地址的差异,它必须适合ptrdiff_t

【讨论】:

  • 这是计数,但是如果您将数组别名为char *(即使数组是int64_t[]),您将获得的计数实际上是以字节为单位的大小,可能会溢出对吧?
  • @CacahueteFrito:如果将两个数组指针相减,则可能发生溢出。如果添加,则不是。
  • 此外,质量实现认识到 ptrdiff_t 的潜在溢出,无论是在源代码级别(技术上是 UB,但程序员没有合理的方法来避免它)和编译器进行的内部转换(无效但很难避免)是一个严重的问题,并且禁止现有对象大于PTRDIFF_MAX
猜你喜欢
  • 2011-12-13
  • 2010-12-25
  • 1970-01-01
  • 2020-02-04
  • 1970-01-01
  • 1970-01-01
  • 2021-04-19
  • 1970-01-01
相关资源
最近更新 更多