存储在C中的指针地址在哪里?

时间:2014-08-08 08:12:55

标签: c variables pointers memory memory-address

我正在学习C并且目前正在学习指针。我理解将一个字节的地址作为变量存储在内存中的原理,这样就可以从内存中获取字节并写入内存地址。

但是,我不明白指针的地址存储在哪里。假设一个指针的值(内存中一个字节的地址)存储在内存中 - 程序如何知道指针的存储位置?难道不需要一个指针指针,导致指针指针无穷无尽的指针......?


更新

实际问题是:“编译器如何为变量分配内存地址”。我发现this question指出了这个话题。

感谢所有回答的人。

5 个答案:

答案 0 :(得分:5)

这是一个实现细节,但是......

并非所有地址都存储在内存中。处理器还有寄存器,可用于存储地址。只有少数寄存器可以这种方式使用,可能是16或32,相比之下,你可以存储在内存中的数十亿字节。

寄存器中的变量

一些变量将存储在寄存器中。例如,如果需要快速添加一些数字,编译器可能会使用例如%eax(x86上的寄存器)来累积结果。如果启用了优化,则变量仅存在于寄存器中是很常见的。当然,在任何给定时间只有少数变量可以在寄存器中,因此大多数变量需要在某个时刻写入内存。

如果变量由于没有足够的寄存器而保存到存储器中,则称为"溢出"。编译器非常努力地避免寄存器溢出。

int func()
{
    int x = 3;
    return x;
    // x will probably just be stored in %eax, instead of memory
}

堆栈中的变量

通常,一个寄存器指向一个名为" stack"的特殊区域。因此,函数使用的指针可以存储在堆栈中,并且可以通过对堆栈指针执行指针运算来计算该指针的地址。堆栈指针没有地址,因为它是一个寄存器,寄存器没有地址。

void func()
{
    int x = 3; // address could be "stack pointer + 8" or something like that
}

编译器选择堆栈的布局,给每个函数一个"堆栈帧"大到足以容纳所有这些函数的变量。如果禁用优化,变量通常会在堆栈帧中获得自己的插槽。启用优化后,将完全重用,共享或优化插槽。

固定地址的变量

另一种选择是将数据存储在固定位置,例如,地址100"。

// global variable... could be stored at a fixed location, such as address 100
int x = 3;

int get_x()
{
    return x; // returns the contents of address 100
}

这实际上并不少见。请记住,"地址100"不一定对应RAM,它实际上是一个虚拟地址,指的是程序虚拟地址空间的一部分。虚拟内存允许多个程序全部使用"地址100",该地址将对应于每个正在运行的程序中不同的物理内存块。

绝对地址也可用于没有虚拟内存的系统,或者用于不使用虚拟内存的程序:引导加载程序,操作系统内核和嵌入式系统软件可以使用没有虚拟内存的固定地址。

绝对地址由编译器通过放置""来指定。在机器代码中,称为重定位

int get_x()
{
    return x; // returns the contents of address ???
              // Relocation: please put the address of "x" here
}

然后,链接器选择x的地址,并将地址放在get_x()的机器代码中。

相对于程序计数器的变量

另一种替代方法是将数据存储在相对于正在执行的代码的位置。

// global variable... could be stored at address 100
int x = 3;

int get_x()
{
    // this instruction might appear at address 75
    return x; // returns the contents of this address + 25
}

共享库几乎总是使用这种技术,它允许在程序的地址空间中可用的任何地址加载共享库。与程序不同,共享库无法选择其地址,因为另一个共享库可能会选择相同的地址。程序也可以使用这种技术,这被称为"位置无关的可执行文件"。程序将独立于缺乏虚拟内存的系统,或者在具有虚拟内存的系统上提供额外的安全性,因为它使得编写shell代码变得更加困难。

就像使用绝对地址一样,编译器会放置一个"空洞"在机器代码中,并要求链接器填写它。

int get_x()
{
    return x; // return the contents of here + ???
              // Relocation: put the relative address of x here
}

答案 1 :(得分:2)

指针只是一个变量。这与...之间的唯一区别一个长变量是我们知道存储在指针变量中的是一个内存地址而不是一个整数。

因此,您可以通过与查找任何其他变量的地址相同的方式查找指针变量的地址。如果将此地址存储在其他变量中,当然这个地址也会有一个地址。

您的混淆似乎源于指针(即可变地址)可以依次存储的事实。但它不必存储在任何地方(只有当你出于某种原因需要这个地址时才会这样做)。从程序的角度来看,任何变量或多或少都是命名的内存位置。所以"指向变量的指针"是一个命名的内存位置,包含应该指向的值" point"到另一个内存位置,因此名称"指针"。

答案 2 :(得分:2)

作为指针的变量仍然是变量,并且像任何其他变量一样。编译器知道变量的位置以及如何访问其值。只是值恰好是一个内存地址,这就是全部。

答案 3 :(得分:1)

  

让我们说一个指针的值(内存中一个字节的地址)存储在内存中

您分配的字节的地址,比如说

char ch = 'a';
编译器在符号表中使用正确的偏移量引用

。在运行时,编译器生成的指令将使用此偏移量将其从主存储器移动到寄存器以对其进行某些操作。

在您提出的意义上,指针不存储在任何地方,当您引用变量的地址时,它只是一种类型,除非您显式创建一个指针变量来存储它,就像这样

&ch;             // address of ch not stored anywhere
char *p = &ch;   // now the address of ch is stored in p

因此这里没有递归概念。

答案 4 :(得分:0)

从编译器的角度来看,无论是声明指针还是通用变量,都只是一个内存空间。当你声明一个变量时,某个内存块被分配给变量。

变量可以是常规变量或指针。 所以最终我们有一个变量(甚至指针只是变量),它们有一个内存位置。

相关问题