8

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

#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

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

乍一看,我认为这是因为程序正在打印物理内存地址,但这篇文章Virtual Memory or Physical Memory反驳了我最初的想法。鉴于程序的执行是“相同的”,没有线程,没有用户输入等,是否有任何理由仍然存在具有不同地址的内存分配?

测试环境:

  • Linux 14.04
  • Mac OS X 10.10
4

1 回答 1

11

在堆上分配时(使用new运算符或malloc()和朋友),您的程序必须要求操作系统分配您的堆内存。操作系统内存管理器中发生了许多幕后的事情(其实现细节大多超出我的工资等级:垃圾收集、回收内存的整合等),这是一件好事,不必考虑它。

局部变量在堆栈上分配。传统上,堆栈分配是可重复的,但近年来这种情况发生了变化。 地址空间布局随机化(ASR) 是 OS 内存管理中一项相对较新的创新,它故意使堆栈分配(例如您观察到的那些)中的内​​存地址在运行时尽可能不确定。这是一个安全特性:这可以防止不良行为者利用堆缓冲区溢出,因为如果 ASLR 实现足够熵,谁知道溢出缓冲区结束时会发生什么?

您为此和其他内存管理功能付出的代价是控制。在现代(非嵌入式)平台上投注分配地址就像玩强力球:可能是一种有趣的分心,但不是未来可行的计划。如果您的代码在 AVR-ISA 平台或类似平台上运行,则可能与 Blackjack 的几率更接近,以至于有人可能会被诱骗获胜(可以这么说)。

无论哪种方式,我个人都不是一个赌博的人——正如我经常说的,绅士们更喜欢堆栈分配。但这基本上就是您获得这些结果的原因。

感谢@TC 的链接和@SergeyA 的建议。

于 2016-01-20T19:40:47.717 回答