为什么局部变量的地址每次都可以不同?

Why the addresses of local variables can be different every time?

我问过 Google 并对 Whosebug 做了一些研究。我的问题是,当我在 C++ 程序中输入 main() 函数并声明第一个变量时,为什么这个变量的地址会随着不同的执行而变化?请看下面我的示例程序:

#include <iostream>

int main() {
    int *a = new int;
    int *b = new int;

    std::cout << "address: " << a << " " << b << std::endl;
    std::cout << "address of locals: " << &a << " " << &b << std::endl;
    return 0;
}

执行结果1:

address: 0xa32010 0xa32030
address of locals: 0x7fff10de2cf0 0x7fff10de2cf8

执行结果2:

address: 0x1668010 0x1668030
address of locals: 0x7ffc252ccd90 0x7ffc252ccd98

执行结果3:

address: 0x10e0010 0x10e0030
address of locals: 0x7ffd3d2cf7f0 0x7ffd3d2cf7f8

如您所见,我在不同的执行过程中得到了不同的结果。输出的第一行对应于分配内存的地址,这应该发生在堆中——如果它们每次都被分配不同的地址,这对我来说有点有意义。然而,即使我打印局部变量的地址——对应于第二行——结果仍然不同。

乍一看,我还以为是因为程序在打印物理内存地址,但是这个post,Virtual Memory or Physical Memory,反驳了我最初的想法。鉴于程序的执行是 "the same,",没有线程、没有用户输入等,是否还有任何原因仍然存在具有不同地址的内存分配?

测试环境:

在堆上分配时(使用 new 运算符或 malloc() 和朋友),您的程序必须要求 OS 分配您的堆内存。在 OS 内存管理器中发生了很多 behind-the-scenes 事情(其实现细节大部分在我的 pay-grade 之上:垃圾收集,回收内存的整合等)并且它是一个 好东西不用去想它。

局部变量分配在栈上。传统上,堆栈分配是可重复的,但近年来这种情况发生了变化。 Address space layout randomization (ASR) 是 OS 内存管理方面的一项相对较新的创新,它在运行时故意使堆栈分配中的内存地址(例如您观察到的)尽可能 non-deterministic 。这是一项安全功能:这可以防止不良行为者利用堆缓冲区溢出,因为如果 ASLR 实现足够熵,谁知道溢出缓冲区的末尾会出现什么?

您为这个和其他 memory-management 功能支付的价格是控制。在现代 (non-embedded) 平台上押注分配地址就像玩强力球:可能是一种有趣的消遣,但不是未来可行的计划。如果您的代码是 运行 在 AVR-ISA 平台或类似的平台上,则赔率可能足够接近 Blackjack,以至于有人可能会被吸引去玩赢(可以这么说)。

不管怎样,就我个人而言,我不是一个赌博的人——正如我常说的,绅士们更喜欢堆栈分配。但这基本上就是您获得这些结果的原因。

感谢@T.C。对于 link 和@SergeyA 的建议。