你怎么知道变量的确切地址?

时间:2012-11-07 03:51:12

标签: c pointers memory-address

所以我正在查看我的C编程教科书,我看到了这段代码。

#include <stdio.h>

int j, k;
int *ptr;

int main(void)
{
    j = 1;
    k = 2;
    ptr = &k;
    printf("\n");
    printf("j has the value %d and is stored at %p\n", j, (void *)&j);
    printf("k has the value %d and is stored at %p\n", k, (void *)&k);
    printf("ptr has the value %p and is stored at %p\n", (void *)ptr, (void *)&ptr);
    printf("The value of the integer pointed to by ptr is %d\n", *ptr);

    return 0;
}

我跑了它,输出是:

  

j的值为1,存储在0x4030e0

  k的值为2,存储在0x403100

  ptr的值为0x403100,存储在0x4030f0

  ptr指向的整数值是2

我的问题是如果我没有通过编译器运行它,你怎么知道这些变量的地址只看这个代码?我只是不确定如何获取变量的实际地址。谢谢!

6 个答案:

答案 0 :(得分:5)

没有其他方法可以在标准C中“知道变量的确切地址”而不是用“%p”打印它。实际地址由许多不受程序员编写代码控制的因素决定。这是操作系统,链接器,编译器,使用的选项以及其他可能的问题。

也就是说,在嵌入式系统领域,有一些方法可以表达此变量必须驻留在此地址,例如,如果外部设备的寄存器映射到地址空间正在运行的程序。这通常发生在所谓的链接器文件映射文件中,或者通过为指针指定一个整数值(使用强制转换)。所有这些方法都是非标准的。

为了您日常的花园种类计划,编写 C 计划的重点是需要而且不应该关心存储变量的位置。< / p>

答案 1 :(得分:3)

你不能。

不同的编译器可以将变量放在不同的位置。在某些机器上,地址不是一个简单的整数。

答案 2 :(得分:2)

编译器只知道诸如“第三个整数全局变量”和“从堆栈指针向下分配36个字节的四个字节”之类的东西。它指的是全局变量,指向子程序(函数),子程序参数和本地变量的指针,只是相对而言。 (别担心C ++中的多态对象的额外内容,哎呀!)这些相对引用作为特殊代码和偏移值保存在目标文件(.o或.obj)中。

链接器可以填写一些细节。在连接多个目标文件时,它可能会修改其中一些粗略的位置引用。当合并来自多个编译单元的全局变量时,全局变量位置将共享一个空间(数据部分);链接器决定它们进入的顺序,但仍然将它们描述为相对于整个全局变量集的开始。结果是一个带有最终操作码的可执行文件,但地址仍然是粗略的并且基于相对偏移量。

直到加载可执行文件后,Loader才会用实际地址替换所有相对地址。现在这是可能的,因为加载器(或它所依赖的操作系统的某些部分)决定在进程的整个虚拟地址空间中存储程序的操作码(文本部分),全局变量(BSS,数据部分)和调用堆栈等。加载器可以进行数学运算,并将实际地址写入可执行文件中的每个位置,通常作为“立即加载”操作码和涉及内存访问的所有操作码的一部分。

谷歌“搬迁表”了解更多信息。有关特定平台的详细说明,请参阅http://www.iecc.com/linker/linker07.html(稍微过时)。

在现实生活中,虚拟地址通过虚拟内存系统映射到物理地址,使用段或其他机制将每个进程保存在单独的地址空间中,这一切都变得复杂。

答案 3 :(得分:1)

我想进一步建立已经提供的答案,指出一些编译器,如Visual Studio,有一个称为地址空间布局随机化(ASLR)的功能,它使程序以随机​​存储器地址开始作为反对 - 病毒功能。鉴于您在输出中的地址,我会说你没有它编译(没有它的程序从地址0x400000开始,我认为)。我的此信息来源是this question的答案。

也就是说,编译器决定了存储局部变量的内存地址。地址很可能会从编译器变为编译器,也可能随源代码的每个版本而变化。

答案 4 :(得分:1)

每个进程都有自己的逻辑地址空间,从零开始。您的程序可以访问的地址都相对于零。仅在将过程加载到主存储器中后,才能确定任何存储器位置的绝对地址。这是通过现代操作系统使用动态重定位完成的。因此,每次将进程加载到内存中时,都可以根据内存的可用性将其加载到不同的位置。因此,允许用户进程知道存储在内存中的数据的确切地址没有任何意义。您的代码输出的是逻辑地址,而不是确切的或物理的地址。

答案 5 :(得分:0)

继续上述答案,请不要忘记进程将在自己的虚拟地址空间中运行(进程隔离)。这可确保当程序损坏某些内存时,其他正在运行的进程不会受到影响。

过程隔离: http://en.wikipedia.org/wiki/Process_isolation

进程间通信 http://en.wikipedia.org/wiki/Inter-process_communication