C利用可变参数列表统计一组数的平均值,利用函数形式参数栈原理实现指针运算
//描述:利用可变参数列表统计一组数的平均值 #include <stdarg.h> #include <stdio.h> float average(int num, ...);//函数原型;即声明 float average2(int num, ...);//num个数 void add(int num, int x, int y, int z); int main(void){ int a=10; int b=20; printf("a地址:%p b地址:%p\n",&a,&b); //宏来实现 float aver = average(4, 10,20,30,40); printf("aver:%.2f\n",aver); //调用指针可变参数来实现 aver = average(4, 10,20,30,40); printf("aver2:%.2f\n",aver); //调用add add(4,20,30,40); /* 输出:说明依次压入栈中的顺序是 可能根据机器不同入栈先后也不同 从下面输出结果看;高地址 n 先入栈;依次是x,y,最后z入栈 n地址:0x7ffff656d8ac x地址:0x7ffff656d8a8 y地址:0x7ffff656d8a4 z地址:0x7ffff656d8a0 */ return 0; } float average(int val, ...) { //定义一个va_list类型的变量用于访问可变参数类别 va_list varlist; int count; float sum = 0; //初始化可变参数列表 va_start(varlist,val); //通过循环获取可变参数列表的参数 for(count=0;count<val;count++){ sum += va_arg(varlist,int); } va_end(varlist); return sum/val; } /*描述:利用函数变量在栈中存储的原理来实现平均数 linux 中函数的参数如 int(int a,int b,int c) 依次是从 右边向左依次 压入栈空间的;所有可以定义一个指针来循环获取每一个可变参数的值 */ float average2(int num,...) { int* p = # float sum = 0; int i=1; for(; i<num;i++){ sum += *(p+i); } return sum/num; } //扩展:使用函数栈的原理 利用指针来操作形式参数 void add(int num, int x, int y ,int z) { //形式参数的地址 printf("n地址:%p\n",&num); printf("x地址:%p\n",&x); printf("y地址:%p\n",&y); printf("z地址:%p\n",&z); }
从上面来看;add函数参数入栈顺序 从左到右LInux 和Windows不一样
进一步发现,Pascal语言不支持可变长参数,而C语言支持这种特色,正是这个原因使得C语言函数参数入栈顺序为从右至左。
具体原因为:C方式参数入栈顺序(从右至左)的好处就是可以动态变化参数个数。
通过栈堆分析可知,自左向右的入栈方式,最前面的参数被压在栈底。
除非知道参数个数,否则是无法通过栈指针的相对位移求得最左边的参数。
这样就变成了左边参数的个数不确定,正好和动态参数个数的方向相反。
因此,C语言函数参数采用自右向左的入栈顺序,主要原因是为了支持可变长参数形式,
C语言中可变参数都是从左到右,所以不管你有多少个参数反正将最右面的那个压入栈底,最左面的参数出入栈顶。换句话说,如果不支持这个特色,
C语言完全和Pascal一样,采用自左向右的参数入栈方式
佛语:我本求心不求佛,了知三界空无物,若欲求佛但求心,只这心心心是佛