仅从内存分配方面来说,普通函数和静态函数有什么区别?
没有区别。静态函数就像全局函数一样,只是作用域不同。
即使对于非静态成员函数,也不需要额外的内存。成员函数`int C::f(int arg1, int arg2)`只是一种语法糖,类似于`int C__f(C* this, int arg1, int arg2)`。
如果成员函数包含一些局部变量怎么办?在这种情况下,函数应该为调用该函数的对象“单独”拥有该变量的副本...
每次调用函数都会有局部变量的副本(除非它们是静态的)。这就是为什么C ++中可以进行递归。
那么如何解决这个问题?
函数调用基于“堆栈帧”。堆栈帧由以下内容组成:
- 函数参数(包括适用的隐式this)。
- 函数中任何其他非静态局部变量。
- “返回地址”,告诉处理器在函数完成后恢复执行的位置。
每次调用函数时,都会创建一个堆栈帧。当函数返回时,堆栈帧将被销毁。如果递归调用函数,则每个递归级别都有自己的堆栈帧。例如,如果您有:
int factorial(int n) {
if (n <= 1)
return 1;
else
return factorial(n - 1) * n;
}
当您调用
factorial(3)
时,会创建一个堆栈帧,如下所示:
------------------------ stack pointer (SP)
n = 3
RA = <in main()>
当递归调用
factorial(2)
时,栈顶会添加一个额外的帧。
------------------------ SP
n = 2
RA = <in factorial()>
------------------------
n = 3
RA = <in main()>
另一个递归调用被执行,调用的是factorial(1)
。
n = 1
RA = <in factorial()>
n = 2
RA = <in factorial()>
n = 3
RA = <in main()>
这是递归的基本情况,返回值1被存储在寄存器中。函数调用完成后,顶部的栈帧被销毁,执行继续在保存的返回地址处。
------------------------ SP
n = 2
RA = <in factorial()>
------------------------
n = 3
RA = <in main()>
现在,调用
factorial(2)
可以计算其返回值(2),然后销毁另一个堆栈帧:
------------------------ SP
n = 3
RA = <in main()>
最后,我们可以计算原始函数调用的结果(6),然后销毁该堆栈帧。