4

C++ 常见问题解答中:

假设一个具有寄存器和堆栈的典型 C++ 实现,寄存器和参数在调用 g() 之前被写入堆栈,然后从 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/ 处理嵌套函数时,有哪些额外的特性/要点需要强调?

谢谢

4

2 回答 2

3

For number 2 this depends on many things including the compiler and the platform. Basically the different ways of passing and returning arguments to functions are called calling conventions. the article Calling conventions on the x86 platform goes into some detail on sequence of operations and you can see how ugly and complicated it gets with just this small combination of platforms and compilers which is most likely why you have heard all sorts of different scenarios, The gen on function calling conventions. covers a wider set of scenarios including 64 bit platforms but is harder to read. It gets even more complicated because gcc may not actually push and pop the stack but directly manipulate the stack pointer, we can see an example of this, albeit in assembly here. It is hard to generalize about calling conventions, if the number of arguments is small enough many calling conventions can avoid using the stack at all and will use registers exclusively.

As to number 3, nested functions does not change anything, it will just repeat the procedure over again for the next function call.

As to number 1 As Sean pointed out .Net compiles to byte code with performs all it's operations on the stack. The Wikipedia page on Common Intermediate Language has a good example.

The x86-64 ABI document is another great document if you want to understand how one specific calling convention works in detail. Figure 3.5 and 3.6 are neat since they give a nice example of a function with many parameters and how each parameter is passed using a combination of general purpose registers, floating point registers and the stack. This sort of nice diagram is a rare find when looking at documents that cover calling conventions.

于 2013-03-20T09:40:30.433 回答
1

1. Although register/stack implementations are the most common underlying implementations of a C++ compiler there's nothing to stop you using a different architecture. For example, you could write a compiler to generate Java bytecode or .NET byte code, in which case you'd have a stack based C++ compiler.

2. When you call f() the typical approach is:

  • Push the return address on the stack and jump to f()

  • In f():

  • Allocate space for the locals x,y and z. This is normally done on the stack. Take a look at this article on call stacks.

  • When you get to g(x,y,z) the compiler will generate code to push the values onto the stack by accessing their values in the stack frame of f(). Note that C/C++ pushes parameters from right to left.

  • When you get to the end of f() the compiler inserts a return instructions. The top of the stack has the address to return to (it was pushed prior to the call to f() )

3. There's nothing special about nested functions as everything follows the same basic template:

  • To call a function - push parameters and call the function.
  • Within the function - allocate space for local variables within a stack

Now this is the general approach. Compilers will introduce their own optimizations to improve performace. For example a compiler may choose to store the first 2 parameters in registers (for example).

NOTE: Although parameter passing by the stack is by far the most common approach there are others. Take a look at this article on register windows if you're interested in finding out more.

于 2013-03-20T09:39:39.717 回答