我读到堆栈帧包含函数的返回地址、函数参数和局部变量。既然函数在编译时不知道它们的堆栈帧在内存中的位置,那么它们如何知道本地变量的内存地址呢?它们是否会在每次读取或写入本地时偏移和取消引用堆栈指针?特别是,在没有指针访问有效支持的嵌入式设备上,加载和存储地址必须硬编码到固件中,指针访问通过保留寄存器,这是如何工作的?
对象
的工作方式是编译器或程序集程序员确定对象的布局——每个字段相对于对象开头的偏移量(以及整个对象的大小(。然后,对象作为引用传递和存储,引用通常是C和机器代码中的指针。在struct xy { int x; int y; }
中,我们可以推断x
处于从对象引用到struct xy
的偏移量0,y
处于偏移量4。
堆栈帧
就像一个包含函数基于内存的局部变量(而不是结构成员(的对象,它不是由对象引用访问的,而是由堆栈或帧指针访问的。(并且通过堆栈指针递减/递增而不是malloc
和free
进行分配/解除分配。(
两者都有一个共同的问题,即在运行时之前,我们不知道动态分配对象的给定字段(x
或y
(的实际位置/地址,也不知道基于内存的局部变量的堆栈帧位置,但当函数运行时,它可以通过将基(对象引用或堆栈/帧指针(与所需项目的相对位置相加,在知道其预定布局的情况下,非常简单地计算(对象字段或基于内存的局部变量的(完整的绝对地址。
处理器提供的寻址模式有助于支持这种访问,通常类似于碱基+位移。
我们还要注意,许多局部变量直接分配给CPU寄存器,因此根本没有内存地址。其他局部变量在内存和CPU寄存器之间移动,这可能被认为是优化,这意味着如果最近已经将变量的值加载到CPU寄存器中时需要变量的值,我们就不必访问内存。
在许多方面,嵌入式设备的处理器与其他处理器一样,提供寻址模式以帮助进行内存访问,并优化编译器,从而可以很好地决定变量的位置。从上面可以看出,并非所有变量都需要存在于内存中,有些变量同时存在于内存和CPU寄存器中,以帮助降低内存访问成本。
anser是,它取决于体系结构。您将有一个寄存器,其中包含当前堆栈帧的地址,例如x86的EBP,一旦您知道了这一点,各个变量就会通过它们在堆栈帧中的偏移量来识别,并在编译时通过对象大小来计算(因此需要在编译时了解局部变量的大小(。
即使堆栈帧出现在内存中的不同位置,变量也会具有相同的相对偏移量,因此您始终可以计算地址。
每个函数的堆栈帧的大小在编译时计算并包含在代码中,这样每个调用都可以设置和清理自己的帧。