微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么即使重启后变量的地址也是相同的?

如何解决为什么即使重启后变量的地址也是相同的?

我试过这个程序是为了知道变量是如何存储的,但有一个疑问。

我编译了上面的代码并创建了一个可执行文件,我记下了输出(a,b,c,d的地址)然后我删除了可执行文件并重新启动了我的电脑,之后当我修改变量的值时并重新执行代码。我发现分配给变量的地址与以前相同。
这是我不明白的,因为变量存储在 RAM 中,而 RAM 是非常量的。

重启后地址怎么还是一样?

#include <stdio.h>
int main(){
    int a=21,b=22,c=23,d=24;
    printf("%p,%p,%p",&a,&b,&c,&d);
}

输出

0xffffcbec,0xffffcbe8,0xffffcbe4,0xffffcbe0

解决方法

有点吹毛求疵,我这样做只是因为我怀疑不精确的措辞背后存在实际误解,这可能会导致您的问题。

当我修改变量的值并重新执行代码时。我发现分配给变量的地址与之前相同。

  • 修改现有变量的值不会改变地址,就像让一个新家庭搬进一栋楼;这不会改变地址。
  • 地址没有分配给变量,值是。 (虽然分配给指针变量的值是地址,但这也不会改变指针变量的地址,只会改变它的值;就像老房子里的一封信,上面写着以前住在那里的家人的新地址,见“链表”;不过老房子的地址还是一样)。
  • 全局变量的地址(与您的代码中的内容不同)是在加载期间确定的(在您执行程序的任何操作之间,例如双击或键入 CLI,以及它的执行),可能有意随机化,但也可能是确定性的。
  • 局部变量的地址(如在您的代码中)是在调用它们和执行它们之间确定的(当时的模型是“堆栈框架设置”),也可能是有意随机化,但也可能是确定性的。

现在是您想知道的核心问题,为什么您在许多情况下观察到的变量地址都相同。

这是不能保证的(但它可能发生,而且不仅是两个随机的 64 位整数碰巧相同)。
即使您在此期间重新启动计算机。
即使您删除并重建了两者之间的可执行文件。
即使两个程序(或同一程序的两个副本)同时运行。
即使这些变量在每个程序中具有不同的值。
即使它们在每个程序中都有不同的类型。
即使这两个输出来自同一个程序的两个进程,并且在同一地址的两个不同类型的变量中具有不同的值。

您的观察也具有误导性,因为在现代 PC 中,您输出的地址相同并不意味着变量位于物理内存中的相同位置。
现代软件(也涉及硬件;查找“PMMU”)的内存管理意味着他们对自己的内存(称为虚拟地址空间)的看法与物理内存/RAM 中发生的情况不同。

实际上,我希望有一些(与安全相关的)机制可以明确地尝试从程序的执行到执行更改变量的位置/地址,即上面提到的有意随机化。
这在 nanofarad 的评论中已被称为“ASLR”,并在此处进行了描述:https://en.wikipedia.org/wiki/Address_space_layout_randomization
我希望它在当今几乎所有环境中的默认设置中都处于活动状态。因此,我怀疑在您的观察期间有一些不寻常的配置在起作用。或者,您可能正在尝试使用非常旧的编译器版本。

但地址相同这一事实在许多情况下确实没有任何意义。如果这些提到的机制不起作用,那么即使您将测试代码移动到不同的函数并从 { {1}},甚至几次;只要您始终观察直接从 main() 输出或始终观察从调用函数的输出(否则见下文)。

如果我可以推测,你可以改变你的程序进行实验,通过使用像你的主函数这样的子函数,一个包装子函数调用那个类似 main 的子函数,然后调用类似 main 的子函数和来自 {{ 1}}。在这种情况下,您的局部变量应该位于两个不同的位置(C 标准实际上并未使用“堆栈”的概念,但这就是我在这里指的模型)。

伪代码:

  • main()
    • 调用类似 main 的子函数
      • 输出局部变量的地址,比如你的代码
    • 调用包装器
      • 调用类似 main 的子函数
        • 输出局部变量的地址,比如你的代码

然后您应该看到不同的地址,不同的值或相同的值都无关紧要。

,

我试过这个程序是为了知道变量是如何存储的,但有一个疑问。

Automatic variables通常位于call stackprocessor registers中。

我对 C 标准的理解(参见例如 n2176 ...)是一个非常聪明的编译器可以在编译时优化您的 printf。您可以编写 GCC plugin 这样做(另请参阅 this 草稿报告)。您的编译器插件可以在编译时将您的 printf 语句替换为接近于 puts("0x1000,0x1008,0x2000,0x2008"); 的内容,并且仍然符合 C 标准。

为什么即使重启后变量的地址也相同?

在 Linux/x86-64 上(例如一些具有默认设置的 Debian,以及作为 C 编译器的 GCC 10),上面的语句是 错误

我编译了您的代码(在 /tmp/prasoon.c 中,在 \n 格式控制字符串的末尾添加了 printf ...)作为 gcc -Wall -g -O /tmp/prasoon.c -o /tmp/prasoon)并且我确实运行了三个次:

rimski.x86_64 ~ 9:16 .0 % /tmp/prasoon
0x7ffdd8cfa2b8,0x7ffdd8cfa2bc,0x7ffdd8cfa2c0,0x7ffdd8cfa2c4
rimski.x86_64 ~ 9:16 .0 % /tmp/prasoon
0x7ffc6d4599e8,0x7ffc6d4599ec,0x7ffc6d4599f0,0x7ffc6d4599f4
rimski.x86_64 ~ 9:16 .0 % /tmp/prasoon
0x7fff288e5168,0x7fff288e516c,0x7fff288e5170,0x7fff288e5174

注意上面的 rimski.x86_64 ~ 9:16 .0 % 是我的 zsh shell 提示符。

说明与ASLR有关。在 Linux 上,使用 proc(5)strace(1)gdb(1)pmap(1) 来了解您的 address spaceprocess

在其他操作系统上,您可以阅读他们的文档以了解如何查询给定进程的地址空间。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。