以下示例取自维基百科。
int arr[4] = {0, 1, 2, 3};
int* p = arr + 5; // undefined behavior
如果我从不解引用p,那么为什么arr + 5就算未定义的行为?我期望指针表现得像整数-唯一的例外是当解引用指针时,指针的值被视为内存地址。以下示例取自维基百科。
int arr[4] = {0, 1, 2, 3};
int* p = arr + 5; // undefined behavior
如果我从不解引用p,那么为什么arr + 5就算未定义的行为?我期望指针表现得像整数-唯一的例外是当解引用指针时,指针的值被视为内存地址。那是因为指针不像整数那样行为表现。这是未定义的行为,因为标准如此规定。
然而,在大多数平台上(如果不是全部),如果您不对数组进行解引用操作,则不会崩溃或遇到可疑的行为。但是,如果您不进行解引用操作,那么进行这种加法的意义何在呢?
也就是说,需要注意的是,一个超过数组末尾一位的表达式在 C++11 规范 §5.7 ¶5 中技术上是 100%“正确”的,并且保证不会崩溃。然而,该表达式的结果是未指定的(仅保证不会出现溢出);而任何超过数组边界一个以上的其他表达式明确会造成未定义的行为。
请注意:这并不意味着从超出一个位置的偏移量读取和写入数据是安全的。您很可能会编辑不属于该数组的数据,并且会导致状态/内存损坏。只是你不会引起溢出异常。
我的猜测是,这是因为不仅解引用是错误的。还有指针算术、比较指针等等。因此,直接说不要这样做比列举可能危险的情况要容易些。
原始的x86存在这样的问题。在16位代码中,指针是16+16位的。如果你添加一个偏移值到低16位,你可能需要处理溢出并改变高16位。这是一个缓慢的操作,最好避免。
在这些系统上,如果offset在范围内(<=数组大小),则可以保证array_base+offset
不会溢出。但是如果array只包含3个元素,则array+5
将会溢出。
这种溢出的结果是,你得到了一个指向数组之前而不是之后的指针。那甚至可能不是RAM,而是映射到内存的硬件。C++标准不试图限制构造指向随机硬件组件的指针时会发生什么,也就是说,在真实的系统上它是未定义行为。
arr
恰好位于机器内存空间的末尾,那么 arr+5
可能超出该内存空间,指针类型可能无法表示该值,即可能会发生溢出,并且溢出是未定义的。int arr[4] = {0, 1, 2, 3};
int* p = arr + 5; // I guess this is allowed to crash, but that would be a rather
// unusual implementation choice on most machines.
*p; //may cause a crash, or it may read data out of some other data structure
assert(arr < p); // this statement may not be true
// (arr may be so close to the end of the address space that
// adding 5 overflowed the address space and wrapped around)
assert(p - arr == 5); //this statement may not be true
//the compiler may have assigned p some other value
arr+5
不是指向结尾的下一个位置,而是指向结尾的下两个位置,因此根据 §5.7 ¶5,它是未定义行为,并且在具有指针陷阱表示的机器上可能会崩溃。 - Jonathan WakelydoSomethingWithItem(someArray+i);
的代码,实现可以捕获任何试图传递任何不指向数组元素或最后一个元素刚刚过去的空间的地址。如果someArray
的分配为额外未使用的元素保留了空间,并且doSomethingWithItem()
仅访问其接收到的指针所指向的项,则实现可以相对廉价地确保上述代码的任何非捕获执行最坏情况下都可以访问未使用的存储。
能够计算“刚过去”的地址会使边界检查比它本来应该的更困难(最常见的错误情况是传递一个指向数组结束位置刚好过去的指针给doSomethingWithItem()
,但只要doSomethingWithItem
不尝试对指针进行解引用操作——调用者可能无法证明这一点——则行为是已定义的)。然而,由于标准允许编译器在大多数情况下保留数组之后的空间,因此这种允许可以使实现限制未捕获错误造成的损害——如果允许更通用的指针算术运算,则这很可能是不可行的。
struct { int a; int x; }
和struct { char x; }
。 - user395760