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

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

本文关键字:局部变量 地址 为什么      更新时间:2023-10-16

我问过谷歌,并对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

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

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

测试环境:

  • Linux 14.04
  • Mac OS X 10.10

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

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

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

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

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