为什么局部变量的地址每次都不一样?

时间:2016-01-20 19:24:16

标签: c++ c memory virtual-memory aslr

我问Google并对StackOverflow做了一些研究。我的问题是,当我在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

正如您所看到的,我在不同的执行中获得了不同的结果。输出的第一行对应于分配的内存的地址,这应该发生在堆中 - 如果每次都为它们分配不同的地址,那对我来说是有意义的。但是,即使我打印局部变量的地址 - 对应于第二行 - 结果仍然不同。

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

测试环境:

  • Linux 14.04
  • Mac OS X 10.10

1 个答案:

答案 0 :(得分:11)

在堆上分配时(使用new运算符或malloc()和朋友),程序必须要求操作系统分配堆内存。操作系统内存管理器中发生了大量的幕后操作(其实现细节大多高于我的工资级别:垃圾收集,回收内存的整合等)并且它是好东西不必考虑它。

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

您为此和其他内存管理功能支付的价格是控制权。在现代(非嵌入式)平台上投注分配地址就像玩强力球:可能是一种有趣的分心,但不是一个可行的未来计划。如果你的代码是在AVR-ISA平台上运行的,或者某些东西,可能是二十一点的可能性更接近,以至于有人可能被诱惑去玩(可以这么说)。

无论哪种方式,我个人都不是赌博者 - 正如我常说的那样,先生们更喜欢堆叠分配。但这基本上就是为什么你得到这些结果。

感谢@ T.C.链接和@SergeyA的建议。