为什么越界指针算术未定义的行为?

NFR*_*RCR 27 c++ undefined-behavior

以下示例来自Wikipedia.

int arr[4] = {0, 1, 2, 3};
int* p = arr + 5;  // undefined behavior
Run Code Online (Sandbox Code Playgroud)

如果我从不取消引用p,那么为什么arr + 5单独的未定义行为?我希望指针表现为整数 - 除了取消引用时,指针的值被视为内存地址.

Luc*_*ore 23

那是因为指针的行为不像整数.它是未定义的行为,因为标准是这样说的.

然而,在大多数平台上(如果不是全部),如果不取消引用数组,则不会发生崩溃或遇到可疑行为.但是,如果你没有取消引用它,那么添加的重点是什么?

也就是说,请注意,在数组末尾的表达式在技术上是100%"正确",并且保证不会因C++ 11规范的§5.75而崩溃.但是,该表达式的结果未指定(只保证不会溢出); 而任何其他表达式超过数组边界超过一个是明确未定义的行为.

注意:这并不意味着从一个一个偏移量读取和写入是安全的.你可能被修改,不属于该阵列和数据导致状态/内存损坏.你不会导致溢出异常.

我的猜测就是这样,因为它不仅仅是解除引用错误.还有指针算术,比较指针等.所以更容易说不要这样做,而不是枚举它可能是危险的情况.

  • @ MahmoudAl-Qudsi标准说它很好,就是这样. (3认同)
  • 增加指向数组最后一个元素的指针的结果不是未指定的;它被指定为刚好经过数组最后一个元素的指针;从这样的指针中减去一个从 1 到数组大小的值将产生一个指向数组元素的有效指针。 (2认同)

MSa*_*ers 15

原始x86可能会出现此类语句的问题.在16位代码上,指针是16 + 16位.如果向低16位添加偏移量,则可能需要处理溢出并更改高16位.这是一个缓慢的操作,最好避免.

在这些系统上,array_base+offset如果偏移量在范围内(<=数组大小),则保证不会溢出.但array+5如果数组只包含3个元素,则会溢出.

这种溢出的结果是你有一个指针,它不会指向数组后面,但之前.这甚至可能不是RAM,而是内存映射硬件.如果构造指向随机硬件组件的指针,即在真实系统上它是未定义的行为,C++标准并不试图限制会发生什么.


Jon*_*ely 5

如果arr恰好位于机器内存空间的末尾,则arr+5可能位于该内存空间之外,因此指针类型可能无法表示该值,即它可能会溢出,并且溢出是未定义的。


Ken*_*oom 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
Run Code Online (Sandbox Code Playgroud)

我敢肯定,您可以在此处添加许多其他示例。