指针的地址存储在哪里?

2024-07-06

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

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

寄存器中的变量

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

如果由于没有足够的寄存器而将变量保存到内存中,则称为“溢出”。编译器非常努力地工作,以避免注册溢出。

代码语言:javascript运行复制int func()

{

int x = 3;

return x;

// x will probably just be stored in %eax, instead of memory

}堆栈上的变量

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

代码语言:javascript运行复制void func()

{

int x = 3; // address could be "stack pointer + 8" or something like that

}编译器选择堆栈的布局,为每个函数提供一个足够大的“堆栈框架”来容纳该函数的所有变量。如果禁用优化,变量通常会在堆栈帧中得到自己的插槽。在启用优化之后,槽将被重用、共享或完全优化。

固定地址的变量

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

代码语言:javascript运行复制// 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

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

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

编译器通过在机器代码中放置一个“孔”来指定绝对地址,称为重新定位。

代码语言:javascript运行复制int get_x()

{

return x; // returns the contents of address ???

// Relocation: please put the address of "x" here

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

相对于程序计数器的变量

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

代码语言:javascript运行复制// 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代码变得更加困难。

就像绝对地址一样,编译器将在机器代码中放置一个“洞”,并要求链接器填充它。

代码语言:javascript运行复制int get_x()

{

return x; // return the contents of here + ???

// Relocation: put the relative address of x here

}