提问人:user3188445 提问时间:3/2/2021 最后编辑:John Kugelmanuser3188445 更新时间:3/6/2021 访问量:6797
C 或 C++ 是否保证数组<数组 + SIZE?
Does C or C++ guarantee array < array + SIZE?
问:
假设你有一个数组:
int array[SIZE];
或
int *array = new(int[SIZE]);
C 或 C++ 是否保证 ,如果是,在哪里?array < array + SIZE
我知道,无论语言规范如何,许多操作系统都通过为内核保留虚拟地址空间的顶部来保证此属性。我的问题是,这是否也由语言来保证,而不仅仅是由绝大多数实现来保证。
例如,假设操作系统内核内存不足,有时会向用户进程提供最高的虚拟内存页,以响应匿名内存请求。如果 或直接调用一个巨大的数组的分配,并且数组的末尾紧邻虚拟地址空间的顶部,从而换行为零,这是否相当于语言的不合规实现?mmap
malloc
::operator new[]
mmap
array + SIZE
澄清
请注意,问题不是问 ,而是数组最后一个元素的地址。那个保证大于 .问题是关于一个经过数组末尾的指针,或者何时是指向非数组对象的指针(所选答案所指向的标准部分清楚地表明,其处理方式相同)。array+(SIZE-1)
array
p+1
p
Stackoverflow 要求我澄清为什么这个问题与这个问题不同。另一个问题是如何实现指针的总排序。另一个问题基本上可以归结为库如何实现,使其甚至适用于指向不同分配对象的指针,标准说这些对象只能进行比较相等,而不是大于和小于。std::less
相比之下,我的问题是,一个数组末尾的数组是否总是保证大于数组。无论我的问题的答案是肯定的还是否定的,实际上都不会改变你的实现方式,所以另一个问题似乎无关紧要。如果与数组末尾的数组进行比较是非法的,那么在这种情况下可能只是表现出未定义的行为。(此外,标准库通常由与编译器相同的人实现,因此可以自由地利用特定编译器的属性。std::less
std::less
答:
C 需要这个。第 6.5.8 节第 5 段说:
指向具有较大下标值的数组元素的指针比指向具有较低下标值的同一数组元素的指针更大
我敢肯定C++规范中有类似的东西。
此要求有效地防止在公共硬件上分配环绕地址空间的对象,因为实现有效实现关系运算符所需的所有簿记是不切实际的。
评论
array + SIZE
array
是的。摘自第 6.5.8 节第 5 段。
如果表达式 P 指向数组对象的元素 表达式 Q 指向同一数组的最后一个元素 对象,指针表达式 Q+1 比较大于 P。
表达式为 P。该表达式指向 的最后一个元素,即 Q。
因此:array
array + SIZE - 1
array
array + SIZE = array + SIZE - 1 + 1 = Q + 1 > P = array
评论
<
与它一致。
array
SIZE
P
Q
数组保证内部有连续的内存空间。在 C++03 左右之后,向量保证也有一个 .这自动意味着您询问的是真的
,它被称为 连续存储 .可以在此处找到 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0944r0.html
的矢量&vec[0] ... &vec[vec.size() - 1]
向量的元素是连续存储的,这意味着如果 v 是向量<T, Allocator>其中 T 是布尔以外的某种类型,那么它服从所有 0 <= n < v.size() 的恒等式 &v[n] == &v[0] + n。据推测,经过五年对连续性与缓存交互作用的研究,WG21 清楚地认识到,需要强制要求连续性,并且应该明确禁止非连续性向量实现。
后者来自标准文档。C++03 我猜对了。
评论
*
&
vec[size()]
vec[size()-1]
[dcl.array]/6
中。但正如丹尼尔·瓦格纳(Daniel Wagner)所指出的那样,仅凭这一点并不能严格证明这一点。array < array + SIZE == true
这是在 C++ 中定义的,来自 7.6.6.4(当前 C++23 草案的第 139 页):
当将具有整数类型的表达式 J 添加到指针类型的表达式 P 中或从中减去时,结果的类型为 P。
(4.1) — 如果 P 的计算结果为 null 指针值,而 J 的计算结果为 0,则结果为空指针值。
(4.2) — 否则,如果 P 指向具有 n 个元素的数组对象 x 的数组元素 i (9.3.4.5),则表达式 P + J 和 J + P(其中 J 的值为 j)指向 x 的(可能假设的)数组元素 i + j,如果 0 <= i + j <= n,表达式 P - J 指向 x 的(可能假设)数组元素 i − j 如果 0 <= i − j <= n。
(4.3) — 否则,行为未定义。
请注意,4.2 明确具有“<= n”,而不是“< n”。对于任何大于 size() 的值,它都是未定义的,但针对 size() 定义。
数组元素的排序在 7.6.9 (p141) 中定义:
(4.1) 如果两个指针指向同一数组的不同元素或其子对象,则需要指向下标较高的元素的指针才能进行比较。
这意味着假设元素 n 将比数组本身(元素 0)大于数组本身(元素 0)对于所有明确定义的 n > 0 的情况。
评论
当为零时,保证不成立。int *array = new(int[SIZE]);
SIZE
的结果必须是一个可以添加到它的有效指针,但在这种情况下,并且严格小于测试将产生 。new int[0]
0
array == array + SIZE
false
评论
SIZE > 0
array <= array + SIZE
C++ 中的相关规则是 [expr.rel]/4.1:
如果两个指针指向同一数组的不同元素或其子对象,则需要指向具有较高下标的元素的指针才能进行比较。
上面的规则似乎只涵盖指向数组元素的指针,而不指向数组元素。但是,如脚注中所述,此处将 one-past-the-end 指针视为数组元素。相关语言规则在 [basic.compound]/3 中:array + SIZE
出于指针算术 ([expr.add]) 和比较 ([expr.rel], [expr.eq]) 的目的,经过 n 个元素数组的最后一个元素末尾的指针被视为等价于指向假设数组元素 n 的指针,并且非数组元素的类型对象被视为属于具有一个 type 元素的数组。
x
x
T
T
所以 C++ 保证(至少当 ),并且对于任何对象。array + SIZE > array
SIZE > 0
&x + 1 > &x
x
评论
for (int *p = array; p < array + SIZE; p++) do_stuff(*p);
&obj < &obj + 1
<