C++数组上指针数学的未定义行为

Jac*_*iba 21 c++ gcc endianness compiler-flags language-lawyer

为什么这个程序的输出是4

#include <iostream>

int main()
{
    short A[] = {1, 2, 3, 4, 5, 6};
    std::cout << *(short*)((char*)A + 7) << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

根据我的理解,在x86 little endian系统上,char有1个字节,短2个字节,输出应该是0x0500,因为数组中的数据A是十六进制的休闲:

01 00 02 00 03 00 04 00 05 00 06 00
Run Code Online (Sandbox Code Playgroud)

我们从开始向前移动7个字节,然后读取2个字节.我错过了什么?

Lig*_*ica 22

您在此违反了严格的别名规则.你不能只读到一个对象的中途,并假装它是一个独立的对象.你不能使用像这样的字节偏移来发明假想的对象.海湾合作委员会完全有权做疯狂的事情,比如回到过去并谋杀埃尔维斯普雷斯利,当你交出你的计划时.

你什么允许做的是检查和处理,使一个任意的对象,使用的字节数char*.使用该权限:

#include <iostream>
#include <algorithm>

int main()
{
    short A[] = {1, 2, 3, 4, 5, 6};

    short B;
    std::copy(
       (char*)A + 7,
       (char*)A + 7 + sizeof(short),
       (char*)&B
    );
    std::cout << std::showbase << std::hex << B << std::endl;
}

// Output: 0x500
Run Code Online (Sandbox Code Playgroud)

(现场演示)

但是你不能仅仅"弥补"原始集合中不存在的对象.

此外,即使您有一个可以被告知忽略此问题的编译器(例如,使用GCC的-fno-strict-aliasing开关),但是对于任何当前的主流架构,组合对象都没有正确对齐.A short不能合法地居住在内存的奇数位置,所以你不能假装那里有一个.没有办法解决原始代码行为的未定义问题; 事实上,如果你通过GCC -fsanitize=undefined切换它会告诉你多少.

我正在简化一点.

  • 你绝对正确的是编译器可以在这种情况下做任何想做的事情.但话说回来,我无法理解,即使禁用了优化,并且通过了"-fno-strict-aliasing",GCC的行为也会有所不同,具体取决于中间的"short"还是"short".*`使用变量. (2认同)
  • @JonathanWakely:两者都是.在解除引用的地址中不存在动态类型为"short"的对象,因此使用"short"类型的左值读取是严格的别名违例.如果您尝试将**写入未对齐的地址,则问题只是对齐. (2认同)
  • @LightnessRacesinOrbit是的,我完全同意问题是试图访问没有对象存在的对象(或者甚至_can_存在于有效程序中,因为对齐).我的观点是,期望`-fno-strict-aliasing`产生任何差异是错误的,因为存在比严格别名更基本的问题.它不是"你访问了一个类型为`int`的对象作为一个类型`short`"它是"你进入世界之间的差距作为一种类型`短",这是Xitalu居住的地方,一个触手,多眼,吞噬灵魂憎恶.不是"短"." `-fno-strict-aliasing`不能取消它 (2认同)

Jon*_*ely 12

由于将指针错误地对齐,程序具有未定义的行为(short*).这打破了C11中6.3.2.3 p6中的规则,这与其他答案中声明的严格别名无关:

指向对象类型的指针可以转换为指向不同对象类型的指针.如果生成的指针未针对引用的类型正确对齐,则行为未定义.

在[expr.static.cast] p13中,C++表示将未对齐转换char*short*给出一个未指定的指针值,该值可能是无效指针,无法解除引用.

检查字节的正确方法是通过char*回送short*并假装存在short一个short无法生存的地址.

  • @Peter ["没有任何硬件架构已经对齐7.此外,7是太小而且只有恶意代码才能尝试访问小号记忆."](https://www.usenix.org/system/files/ 1311_05-08_mickens.pdf).对不起 - 忍不住,但米克斯太可笑了,不能分享. (7认同)
  • 规则被破坏的事实"与严格别名无关"并不意味着别名是可以的.它只意味着破坏了多个规则.这在"short"没有对齐要求的平台上尤为重要 - 代码仍然存在. (3认同)