C++(嵌套)函数调用指令 - 寄存器

问题描述:

C++ FAQC++(嵌套)函数调用指令 - 寄存器

假设一个典型的C++实现具有寄存器和一个栈, 寄存器和参数获得只是 呼叫之前被写入堆栈克(),然后从g() 中的堆栈中读取参数并在g()返回f()时再次读取以恢复寄存器。

关于嵌套函数调用

void f() 
{ 
    int x = /*...*/; 
    int y = /*...*/; 
    int z = /*...*/; 
    ...code that uses x, y and z... 
    g(x, y, z); 
    ...more code that uses x, y and z... 
} 

1 /与寄存器的C++实现所有和堆栈?这是否意味着:实现取决于编译器/处理器/计算机体系结构?

2 /当我拨打f()时,什么是指令序列(没有汇编语言,只是大图)?我已经读过关于这个主题的分歧性的东西,我也不记得那里提到的那个寄存器,但只有堆栈。

3当处理嵌套函数时,有什么额外的特性/要强调的要点?

感谢

+0

“嵌套函数”?你的意思是“函数调用”?在C++中调用一个函数并不是什么特别的。实际上,这是一种相当常见的编程方式。 – 2013-03-20 09:12:42

+6

“是否所有C++的寄存器和堆栈实现?”第 – 2013-03-20 09:15:00

+2

“当我打电话给f()时发生了什么?” f()被调用并且流向那里,这不是真正的火箭科学 – 2013-03-20 09:15:44

对于数字2这取决于许多事情,包括编译器和平台。基本上,向函数传递和返回参数的不同方式称为calling conventions。文章Calling conventions on the x86 platform详细介绍了操作的顺序,您可以看到只有这些平台和编译器的小组合才能得到的复杂程度,这很可能就是为什么您听说过各种不同的场景,The gen on function calling conventions.涵盖了更广泛的场景包括64 bit平台,但难以阅读。它变得更加复杂,因为gcc可能实际上并不是pushpop的堆栈,而是直接操作堆栈指针,我们可以看到这个例子,尽管在汇编here。如果参数的数量足够小,许多调用约定可以完全避免使用stack,并且将仅使用registers,这很难概括为调用约定。

至于编号3,嵌套函数不会改变任何东西,它只会重复下一次函数调用的过程。

至于数字1正如肖恩指出.Net编译为字节码与执行所有它在堆栈上的操作。*页面Common Intermediate Language就是一个很好的例子。

x86-64 ABIdocument是另一个伟大的文件,如果你想了解一个特定的调用约定如何工作的细节。 Figure 3.5 and 3.6是很整洁的,因为它们给出了一个很好的例子,它提供了许多参数的功能,以及如何使用general purpose registers,floating point registersstack的组合传递每个参数。当查看涵盖调用约定的文档时,这种很好的图很难找到。

1.虽然寄存器/栈的实现是一个C++编译器的最常用的底层实现没有什么可以阻止你使用不同的体系结构。例如,您可以编写一个编译器来生成Java字节码或.NET字节码,在这种情况下,您将拥有基于堆栈的C++编译器。

2.当你调用f()的典型做法是:

  • 推栈,并跳转到f()的返回地址

  • 在F():

  • 为当地人x,y和z分配空间。这通常在堆栈上完成。看看this article的调用堆栈。

  • 当你到达g(x,y,z)时,编译器将生成代码,通过访问它们在f()的栈帧中的值来将值推入堆栈。请注意,C/C++从右向左推参数。

  • 当你到达f()的末尾时,编译器会插入return指令。堆栈的顶部有返回地址(这是之前调用到f推())

3.有什么特别的嵌套函数作为一切遵循相同的基本模板:

  • 调用函数 - 推送参数并调用函数。
  • 在函数 - 分配堆栈

中的局部变量空间现在,这是一般的方法。编译器将引入他们自己的优化来提高性能。例如,编译器可以选择将前2个参数存储在寄存器中(例如)。

注意:虽然堆栈传递的参数是迄今为止最常用的方法,但还有其他方法。如果您有兴趣了解更多信息,请参阅register windows上的这篇文章。