#include<iostream>
using namespace std;
int main(){
int arr[] = {1,2,3};
printf("outside loop trail 1: arr[-1] = %d \n", arr[-1]);
for(int i = 0; i<10; i++){
printf("\ninside loop trail i = %d, arr[-1] = %d \n", i, arr[-1]);
}
}
问题:
为什么循环内的输出是序列0、1、2(与循环索引i相同);为什么?但是每次执行代码时,循环外的输出都会改变吗?谢谢!
在
之后输出g++ -o explore explore.cpp && ./explore
外部循环路径1:arr [-1] = 537839344
内部循环跟踪i = 0,arr [-1] = 0
内部循环路径i = 1,arr [-1] = 1
内部循环路径i = 2,arr [-1] = 2
第二次运行./explore:
外部循环路径1:arr [-1] = 1214220016
内部循环跟踪i = 0,arr [-1] = 0
内部循环路径i = 1,arr [-1] = 1
内部循环路径i = 2,arr [-1] = 2
答案 0 :(得分:5)
这实际上是标准所涵盖的。例如,C++17 [expr.add] /4
指出:
将具有整数类型的表达式添加到指针或从指针中减去时,结果将具有指针操作数的类型。如果表达式
P
指向具有x[i]
个元素的数组对象x
的元素n
,则表达式P + J
和J + P
(其中{{如果J
,则1}}的值j
)指向(可能是假设的)元素x[i + j]
;否则,行为是不确定的。
我讨论添加指针和整数的原因是因为0 <= i + j <= n
的{{1}}和array[index]
的等价性(如下标中的*(array + index)
一样,不是减法):
表达式
C++17 [expr.sub] /1
与sub
相同(根据定义)。
现在要花很多钱,但是从根本上讲,这意味着添加“指向数组元素的指针”和“索引”的结果为您提供了一个必需指向的指针数组中的一个元素或刚好在最后一个元素之外的元素(1)。
由于第一个指针(E1[E2]
之前 的指针不不能满足该要求,因此它的行为不确定。一旦这样做,所有的赌注都将消失,实现可以自由地做自己喜欢的事情。您可以算是幸运的,它在玩*((E1)+(E2))
后没有擦除硬盘:-)
请注意,带有负索引本身并没有错,以下代码为您提供了第二个元素(最后的“指针”仍在数组中):
array[-1]
(1)注意,如果您不尝试取消引用,则允许 pointer 指向数组的正上方。不幸的是,derisive_laughter.ogg
是取消引用操作,因此尽管int array[100];
int *ptrThird = &(array[2]);
int second = ptrThird[-1];
有效,而array[index]
却无效。
答案 1 :(得分:2)
这是未定义的行为。
答案 2 :(得分:2)
通常来说,以这种方式使用的数组索引等同于进行指针数学运算:
arr[n] -> *(arr+n)
通过使用负索引,您可以在与数组数据关联的存储块开始之前引用内存。如果您使用的索引超出了数组的范围,则结果是 undefined 。
答案 3 :(得分:2)
后缀表达式后跟方括号中的表达式是后缀表达式。这些表达式之一应为类型“ T的数组”的glvalue或类型为“指向T的指针”的prvalue,另一个表达式应为无作用域枚举或整数类型的prvalue。结果为“ T”类型。类型“ T”应为完全定义的对象类型。 表达式E1 [E2](根据定义)与*((E1)+(E2))相同,除了对于数组操作数而言,如果该操作数为,则结果为左值一个左值和一个x值,否则。表达式E1在表达式E2之前排序。
将具有整数类型的表达式J添加到指针类型的表达式P或从中减去时,结果的类型为P。
(4.1)如果P的结果为空指针值,J的值为0,则结果为空指针值。
(4.2)否则,如果P指向具有n个元素的数组对象x的元素x [i],则表达式P + J和J + P(其中J的值为j)指向元素x(可能是假设的) [i + j],如果0≤i + j≤n,并且表达式P-J指向(可能是假设的)元素x [i-j],如果0≤i-j≤n。
(4.3)否则,行为是不确定的。