作者:飞飞鱼531 | 来源:互联网 | 2023-10-15 22:59
假设你有一个数组:
int array[SIZE];
或者
int *array = new(int[SIZE]);
C 或 C++ 是否保证array ,如果是,在哪里?
我知道无论语言规范如何,许多操作系统通过为内核保留虚拟地址空间的顶部来保证此属性。我的问题是语言是否也保证了这一点,而不仅仅是绝大多数实现。
举个例子,假设一个操作系统内核处于低内存中,有时会向用户进程提供虚拟内存的最高页面,以响应mmap
对匿名内存的请求。如果malloc
或::operator new[]
直接要求mmap
分配一个巨大的数组,并且数组的末尾紧靠虚拟地址空间的顶部,从而array + SIZE
环绕为零,这是否等于该语言的不合规实现?
澄清
请注意,问题不是问 about array+(SIZE-1)
,它是数组最后一个元素的地址。那一个保证大于array
。问题是关于超过数组末尾的指针,或者p+1
何时p
是指向非数组对象的指针(所选答案指向的标准部分明确表示以相同方式处理)。
计算器要我解释,为什么这个问题是不一样的这一个。另一个问题询问如何实现指针的总排序。另一个问题本质上归结为一个库如何实现std::less
它甚至可以用于指向不同分配对象的指针,标准说只能比较相等,而不是大于和小于。
相比之下,我的问题是关于数组末尾的一个是否总是保证大于数组。我的问题的答案是或否实际上并没有改变你将如何实施std::less
,所以另一个问题似乎并不相关。如果与数组末尾的比较是非法的,那么std::less
在这种情况下可能只是表现出未定义的行为。(此外,通常标准库是由与编译器相同的人实现的,因此可以自由利用特定编译器的属性。)
回答
是的。来自第6.5.8节第 5 段。
如果表达式 P 指向数组对象的一个元素,而表达式 Q 指向同一个数组对象的最后一个元素,则指针表达式 Q+1 比较大于 P。
表达式array
是 P。表达式array + SIZE - 1
指向 的最后一个元素array
,也就是 Q。因此:
array + SIZE = array + SIZE - 1 + 1 = Q + 1 > P = array
@Wyck - it doesn&#039;t prohibit such an implementation, _as long as it ensures that `<` is consistent with it_.
@Wyck, you might not be able to put anything at the top position of the address space, if I&#039;m reading cppreference.com correctly: ["a pointer to an object that is not an element of an array is treated as if it were pointing to an element of an array with one element"](https://en.cppreference.com/w/c/language/operator_comparison)
@ilkkachu: Objects whose address is not taken could be placed at the top of address space or at whatever physical address would match a null pointer&#039;s representation. Since most non-trivial programs will have at least two objects whose address is not taken, a requirement that any objects whose address is taken have to go elsewhere doesn&#039;t reduce the amount of practically useful storage.
Does this imply that you cannot create an implementation that puts an array at the top of the address space? Because (array= ((int*)0xFFFFFFFC))+ 1 might be 0x00000000? (32-bit address space, 4-byte int example)
@Wyck : You seem to be conflating the runtime values of the variables `array`, `SIZE`, `P`, and `Q` with actual virtual memory addresses. Sure, having pointers contain bit patterns identical to virtual memory addresses is an easy implementation, but it is not mandatory. As a concrete example, a pointer to a (16-bit) word at an odd address on an MC68000 cannot be directly dereferenced since [non-byte dereferencing an odd address on that architecture throws exceptions](http://mrjester.hapisan.com/04_MC68/Sect01Part06/Index.html).
@EricTowers how is that a counterexample? A conforming C implementation just wouldn&#039;t allow objects to be created on odd addresses. We call that *alignment*.
@EricTowers: A conforming implementation is required to ensure that it allocates objects with an alignment that will be compatible with whatever means it uses to accesses them. If e.g. a hardware platform has a 32-bit load instruction that works with arbitrarily-aligned addresses, and a load-multiple instruction that only works with 32-bit-aligned addresses, an implementation could at its leisure either ensure that 32-bit objects are aligned and use both kinds of instructions to access them, or use only the former kind of instruction but then place objects with arbitrary alignment.
回答
C 需要这个。第6.5.8节第 5 段说:
指向具有较大下标值的数组元素的指针比较大于指向具有较小下标值的同一数组元素的指针
我确信 C++ 规范中有类似的东西。
这一要求有效地防止了在公共硬件上分配环绕地址空间的对象,因为实现有效实现关系运算符所需的所有簿记是不切实际的。
@Neil You&#039;re allowed to form a pointer just past the end, but not allowed to dereference it.
Note, that `array + SIZE` does not point to any element of `array`. It points to element just after the last one.
The “bookkeeping necessary to implement the relational operator efficiently” is trivial: p q are equivalent to p−q <0, p−q = 0, and p−q > 0, where p−q is computed in the width of the address space bits. As long as every supported object is less than half the size of the address space, p−q must fall in the right region.
回答
int *array = new(int[SIZE]);
当SIZE
为零时,保证不成立。
的结果new int[0]
必须是一个可以0
添加到它的有效指针,但array == array + SIZE
在这种情况下,严格小于测试将产生false
。
You got me, I should have specified I was assuming `SIZE > 0`...
回答
这是在 C++ 中定义的,来自 7.6.6.4(当前C++23 草案的p139 ):
当一个整数类型的表达式 J 与指针类型的表达式 P 相加或减去时,结果的类型为 P。
(4.1) — 如果 P 的计算结果为空指针值而 J 的计算结果为 0,则结果为空指针值。
(4.2) — 否则,如果 P 指向具有 n 个元素的数组对象 x 的数组元素 i (9.3.4.5),则表达式 P + J 和 J + P(其中 J 的值为 j)指向(可能-假设)数组元素 i + j of x 如果 0 <= i + j <= n 并且表达式 P - J 指向(可能是假设的)数组元素 i ?如果 0 <= i ? j <= n。
(4.3) — 否则,行为未定义。
请注意,4.2 明确具有“<= n”,而不是“数组元素的排序在 7.6.9 (p141) 中定义:
(4.1) 如果两个指针指向同一个数组的不同元素,或者指向其子对象,则要求指向下标较高的元素的指针比较大。
这意味着对于 n > 0 的所有明确定义的情况,假设元素 n 将比数组本身(元素 0)更大。
回答
C++ 中的相关规则是[expr.rel]/4.1:
如果两个指针指向同一个数组的不同元素,或者指向其子对象,则需要指向下标较高的元素的指针比较大。
上面的规则似乎只涵盖了指向数组元素的指针,并array + SIZE
没有指向数组元素。然而,正如脚注中提到的,一个最后一个指针在这里被视为一个数组元素。相关的语言规则在[basic.compound]/3 中:
为指针运算的目的([expr.add])和比较([expr.rel],[expr.eq]),过去的阵列的最后一个元件的端部的指针x
的Ñ元件被认为是等同于指向假设数组元素n的指针x
和类型T
不是数组元素的对象被认为属于具有一个类型元素的数组T
。
所以 C++ 保证array + SIZE > array
(至少当SIZE > 0
),以及&x + 1 > &x
任何对象x
。