CPP {函数参数的入栈过程}
@LOC: 1
函数参数的入栈过程
定义
void F( int, int){}
int G( int _a){
return _a;
}
F( G(1), G(2));
最先执行的是G(2)
, 然后才是G(1)
, 即函数参数的执行次序是 从右往左的;(很反直觉呀。。。 所以最好函数参数 不要写成函数调用)
性质
int Data = 0;
int F0(){ Data ++; return -1;}
int F1(){ Data += 100; return -2;}
void Func_0( int v0, int v1, int v2, int v3, int v4){ DE_(v0,v1,v2,v3,v4);}
void Func_1(){}
template< class _H, class... _T> void Func_1( const _H & _h, const _T & ... _t){ cout<< _h<< ","; if( sizeof...( _t) > 0){ Func_1( _t...);}}
Func_0( Data, F0(), Data, F1(), Data);
的结果是: 101, -1, 100, -2, 0
;
.
只要不是变长函数 其他函数包括模板函数 都是这个结果, 比如即使是模板函数 其实他的实例化 在编译期就完成了 本质上也属于普通函数;
Func_1( Data, F0(), Data, F1(), Data);
的结果是: 101, -1, 101, -2, 101
; (是不是很震惊…);
.
变长函数 很复杂, 他的实例化函数的类型是template<> Func_1< int, <int,int,int,int> >
(很复杂把…);
虽然说 所有函数的参数的入栈(入程序栈, 程序的执行 本质上就是对程序栈的操作) 是从右往左的, 对于变长函数 他也不例外 但是他还是很特别的… 具体机制如下:
普通函数Func_0( Data, F0(), Data, F1(), Data):
参数*从右往左* 依次入栈:
0: Data(此时等于0)入栈, 即`v4==0`;
1: `auto ret=F1()` (即执行F1函数), 将ret入栈 即`v3==ret==-2`;
2: Data(此时等于100)入栈, 即`v2==100`;
3: `auto ret=F0()` (即执行F0函数), 将ret入栈 即`v1==ret==-1`;
4: Data(此时等于101)入栈, 即`v0==101`;
@DELI;
变长函数Func_1( Data, F0(), Data, F1(), Data): (这是我根据现象 反向猜测的)
0: 由于参数个数未知 首先得确定参数个数;
0: *从左到右*递归的遍历所有参数[Data -> F0() -> ...] (遇到函数不执行 因为此时我们的目的 只是要确定参数个数);
此时 我们计算出了*参数个数*;
1: 在递归的回溯过程时 遇到*参数*为函数的 即`F1(),F0()`(注意次序) 执行该函数 且用其返回值来*替换*;
此时 函数变成了*普通函数*: Func_1<int,int,int,int,int>( Data, ret_F0, Data, ret_F1, Data);
1: 执行普通函数`Func_1( Data, ret_F0, Data, ret_F1, Data);` (即参数从右往左入栈);
0: Data(此时等于101)入栈, 即`v4==101`;
1: ret_F1(此时等于-2)入栈, 即`v3==-2`;
2: Data(此时等于101)入栈, 即`v2==101`;
3: ret_F0(此时等于-1)入栈, 即`v1==-1`;
4: Data(此时等于101)入栈, 即`v0==101`;
错误
@MARK: @LOC_0
;
对于Func( D, F0(), D, F1(), D)
, 千万不要以为是: Func( D(最初值), F0(), D(受F0函数影响后), F1(), D(依次受[F0,F1]函数影响后))
;
.
无论是什么函数, 他的参数入栈 都是从右往左的!! 很容易犯这个错误, 以为是从左到右 依次生效的…
笔记
Func( D, F(), D, H(), D);
栈底[D, H(), D, F(), D]
, 也就是 F,H函数
不是在最一开始就执行的, 而是等入栈时遇到才执行!
也即是 千万不要以为: 第一个D值 是不受F/H函数
影响的 这是错误的;
他的结果 正好与Func_0( vector<int>)
即Func_0( {D, F(), D, ...})
的结果 相反, 从此可知 初始化列表的构造方式 是从左到右的;
但是如果Func
是变长模板函数, 那么F,H函数
会在最一开始就执行, 且以H->F
的顺序执行; 1,-1,1,-1,1
(跟变长宏无关 即与__VA__ARGS__
无关, 也就是 DE_( D, H(), D, ...)
和Func( D, D(), D, ...)
是完全一致的;
我认为他的底层是: 先倒序遍历一遍 目的是确定各个参数的类型, 即得到Func<int,int,....>
类型 然后实例化, 变成普通函数 然后就执行普通函数的入栈过程;