指针的地址存储在哪里?
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
}