如果程序计数器指向要执行的下一条指令的地址,帧指针的作用是什么?
答案 0 :(得分:6)
一些局部变量和参数的存储通常在堆栈帧中分配,只需在函数调用后将堆栈指针弹回其原始级别即可自动释放。
但是,经常调整堆栈指针,以便将参数推送到堆栈以获取新的调用级别,并且在进入方法时至少调整一次以分配其自己的局部变量。调整堆栈指针还有其他更为模糊的原因。
所有这些调整都使得偏移的使用变得复杂,以获得参数,本地,以及在某些语言中,中间词法范围。编译器跟踪可能并不太难,但如果程序正在调试,那么调试器(人或程序)也必须跟踪变化的偏移量。
如果技术上是不必要的开销,只需分配一个寄存器指向当前帧就更简单了。在x86上,这是%ebp
。在进入函数时,它可能与堆栈指针有固定的关系。
除了调试之外,这还简化了异常管理,甚至可以通过消除或优化对堆栈指针的一些调整来收回成本。
你提到了程序计数器,所以值得注意的是,通常框架指针是一个完全软件构造,而不是硬件实现的东西,除非几乎每台机器都可以执行寄存器+偏移寻址模式。像x86这样的某些机器确实以寻址模式和宏指令的形式提供了一些硬件支持,用于创建和恢复帧。但是,有时会发现核心指令更快,宏操作最终被弃用。
答案 1 :(得分:5)
这不是一个真正的C问题,因为它完全依赖于编译器。
然而,堆栈帧是考虑当前函数及其父函数的有用方法。通常,帧指针指向堆栈上的特定位置(对于给定的堆栈深度),您可以从中定位传入的参数以及局部变量。
这是一个例子,假设您调用一个函数,它接受一个参数并返回1和该参数之间所有数字的总和。 C代码类似于:
unsigned int x = sumOf (7);
: :
unsigned int sumOf (unsigned int n) {
unsigned int total = 0;
while (n > 0) {
total += n;
n--;
}
return total;
}
为了调用此函数,调用者将7推入堆栈,然后调用子例程。函数本身设置帧指针并为局部变量分配空间,因此您可以看到代码:
mov r1,7 ; fixed value
push r1 ; push it for subroutine
call sumOf ; then call
retLoc: mov [x],r1 ; move return value to variable
: :
sumOf: mov fp,sp ; Set frame pointer to known location
sub sp,4 ; Allocate space for total.
: :
此时(sub sp,4
之后),您有以下堆栈区域:
+--------+
| n(7) |
+--------+
| retLoc |
+--------+
fp -> | total |
+--------+
sp -> | |
+--------+
你可以看到你可以通过使用帧指针上方的地址和帧指针下面的局部变量来找到传入的参数。
该函数可以使用[fp+8]
访问传入的值(7),fp+8
的内存内容(在这个例子中,每个单元格都是4个字节)。它还可以使用total
访问自己的本地变量([fp-0]
),内容位于fp-0
。我已使用fp-0
命名法,即使减去零也没有效果,因为其他本地人会有相应的较低地址,如fp-4
,fp-8
等等。
当您在堆栈中上下移动时,帧指针也会移动,并且通常在调用函数之前将前一帧指针压入堆栈,以便在离开该函数时轻松恢复。但是,虽然堆栈指针在函数内可能会疯狂移动,但帧指针通常保持不变,因此您始终可以找到相关变量。
答案 2 :(得分:3)
良好的讨论here,以及所有例子。
简而言之:FP指向堆栈中函数框架内的固定点(并且在函数执行期间不会改变),因此所有传递参数和函数的本地(“自动”)变量都可以通过偏移来访问来自FP(虽然SP可以在函数执行期间改变,而PC肯定会改变; - )。
答案 3 :(得分:1)
通常是返回地址(例如,有时只是过去的最后一个参数)。关键是帧指针在方法的生命周期内是固定的,而堆栈指针可以在执行期间移动。
这是非常依赖于实现的(更多的是机器概念,而不是真正的语言概念)。
从您提供给另一个答案的评论中解除:
Woh ... Stack Pointer?...是程序计数器的同义词吗?
了解call stack。基本上,调用堆栈存储当前方法的本地数据(局部变量,方法的参数和返回调用者的地址)。堆栈指针指向该结构的顶部,该结构是分配新空间的位置(通过将堆栈指针移动到“更高”)。
答案 4 :(得分:0)
帧指针指向当前帧(当前本地函数)中的内存区域,通常指向当前本地函数的返回地址。
答案 5 :(得分:-1)
由于没有人对此做出回应,我会试一试。帧指针(如果存储器服务)是堆栈的一部分以及堆栈指针。堆栈由堆栈帧(有时称为激活记录)组成。堆栈指针指向堆栈的顶部,而帧指针通常指向帧结构中的某个固定点,例如返回地址的位置。有更详细的描述以及维基百科上的图片。