【1】函数调用时形参的压栈顺序
1、示例代码如下(VS2010):
#include <iostream>
using namespace std; void fun(int a, int b, int c = ); void fun(int a, int b, int c) // 这种情况不可以写,因为函数声明已写
{
cout << "a :: " << a << endl;
cout << "b :: " << b << endl;
cout << "c :: " << c << endl;
} class A
{
public:
void fun1(int a, int b);
void fun2(int b, int c = );
}; void A::fun1(int a, int b = ) // 这种情况可以写
{
} void A::fun2(int b, int c) // 这种情况下不可以写,因为函数声明已写
{
} void main()
{
int n = ;
fun(n * n, n, n++); system("pause");
} // run out:
/*
a :: 121
b :: 11
c :: 10
请按任意键继续. . .
*/
分析:
从输出的结果琢磨,a如果等于10 * 10 = 100,说明是先压栈参数a。
然后,再压栈参数b,b = n, 那么b等于10。
最后,再压栈参数c,c = n++,即c等于10。而n最终等于11。
但是,还得用客观事实说明问题:
首先,压栈形参c,c = n++,即c等于10。而n执行完后等于11。
然后,压栈形参b,b = n,即b等于11。
最后,再压栈形参a,a = n * n, 即a等于121。
2、有人说还看得不太明白,云里雾里的。那么请再看如下实例分析。
代码如下:
#include <iostream>
using namespace std; class TestA
{
public:
TestA(int a = , int b = , int c = , int d = )
: m_nA(a)
, m_nB(b)
, m_nC(c)
, m_nD(d)
{} int getA() { cout << "TestA::getA() [" << m_nA << "]" << endl; return m_nA; }
int getB() { cout << "TestA::getB() [" << m_nB << "]" << endl; return m_nB; }
int getC() { cout << "TestA::getC() [" << m_nC << "]" << endl; return m_nC; }
int getD() { cout << "TestA::getD() [" << m_nD << "]" << endl; return m_nD; } private:
int m_nA;
int m_nB;
int m_nC;
int m_nD;
}; int sumFunc(int a, int b, int c, int d)
{
return (a + b + c + d);
} void main()
{
TestA aObj;
cout << "调用顺序及求和结果如下:" << endl;
cout << sumFunc(aObj.getA(), aObj.getB(), aObj.getC(), aObj.getD()) << endl;
system("pause");
} // run out:
/*
调用顺序及求和结果如下:
TestA::getD()[40]
TestA::getC()[30]
TestA::getB()[20]
TestA::getA()[10]
100
请按任意键继续. . .
*/
通过调用全局函数sumFunc,分析其形参的压栈先后顺序。
【2】为什么函数形参默认值需要从最后一个赋值?
从上一步的研究结果发现:
函数调用时,首先压栈最后一个形参,若有一个已经确定默认值或常规调用可以忽略除非在特殊情况下才考虑的形参,那么置为最末一个形参。
建议在声明函数时,最好加上默认值。定义函数时,不需要再加默认值(因为编译器会报重定义默认参数值的编译错误)。
(备注:其实,声明不加定义加也行,但是为了便于代码的阅读与维护,建议声明加定义不加。)
反证法。假设不从最后一个赋默认值,那么试想一下,编译器就会匹配不到最后一个形参的值,报编译错误。
【3】总结
压栈形参由后向前,赋默认值也应由后向前。
Good Good Study, Day Day Up.
顺序 选择 循环 总结