我想知道,当我们按值调用函数时,它会生成变量的副本,但是该变量的副本存储在内存中的哪个位置
2 回答
它通常位于堆栈上,位于函数调用的指针或函数的堆栈帧之前。我不知道这是否是标准要求的,但它可能是普遍完成的。
所以举个例子:
int f(myClass byVal)
{
int b;
...
};
...
myClass myInst;
int val = f(myInst)
我希望堆栈看起来像 f 内部的省略号点:
<top> b (stack frame inside of f)
pointer to f
temp copy of myInst
myInst (stack frame of outer function).
...
当 f 返回时,堆栈被清理到封闭函数的帧。
值得注意的是 jogojapan 的评论,即优化可能会导致更改,包括将一些数据放入机器内部寄存器中。你永远不会想指望这样的内部细节,但最好了解常用的机制。
就像 Codie CodeMonkey 所说,在大多数当前的计算机上,副本将在堆栈上。但是,也有明显的例外:
在具有相当数量寄存器的平台上(旧的 PowerPC 和整个 Power 系列就是一个例子,另一个是 Sparc,我将“相当数量”定义为至少 32 个寄存器),副本实际上是由一个寄存器制作的到另一个寄存器。在这些平台上,有严格的规则,哪些注册函数可以更改,哪些不能更改。局部变量通常保存在寄存器中,任何调用的函数都不能更改它们,因此它们不需要内存访问。只有当被调用函数决定它需要使用一些它不能更改的寄存器时,它才会在覆盖之前将这些寄存器的内容保存到堆栈中。
因此,此类平台上价值的典型生命是这样的:
函数 a 将其写入一个寄存器 (r31),该寄存器可能不会被函数调用更改。
函数 a 将值复制到用于寄存器传递 (r3) 的寄存器中。两个副本都驻留在寄存器中。
函数 a 调用函数 b。
函数 b 需要调用函数 c,然后调用它仍然需要值。所以它需要一些寄存器并将它们保存在堆栈中(包括最初保存我们值的寄存器 r31)。现在有该值的三个副本:两个在寄存器中,一个在堆栈中。
函数 b 将值复制到它的旧寄存器 (r3 -> r31) 中,但不知道它已经存在。它调用函数 c。在此调用之后,堆栈上仍有一份保存的副本,而寄存器 r31 中还有一份。
函数 b 对我们的值做任何它需要做的事情。最后,它通过从堆栈中加载它们的旧值来恢复它不应该修改的寄存器。从函数 b 的角度来看,这会破坏它在寄存器 r31 中值的工作副本,但是,我们仍然在寄存器中保留一份值,在堆栈中保留一份。
函数 b 返回,破坏了它的堆栈分配,值的堆栈副本逐渐消失。只有函数 a 中值的原始副本保留在它应该在的位置:在寄存器 r31 中。
尽管这种方法看起来很复杂,但与 X86 方法相比,它需要的堆栈内存访问次数要少得多。特别是不需要调用任何其他函数的叶例程,根本不需要访问堆栈。