可变参函数
Posted suzkfly
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了可变参函数相关的知识,希望对你有一定的参考价值。
最常见的可变参函数就是printf了,它的函数原型为
int printf(const char *format, ...);
可变参函数至少要有一个确定的参数,后面的...表明该函数第一个参数之后还可以输入若干个参数。
为解析可变参数,C语言提供了一个va_list类型和,并在stdarg.h中声明了四个函数,分别是va_start, va_arg, va_end, 和va_copy,可以用输入命令:man stdarg 看到它们:
下面以一个简单的例子讲解:
1 #include <stdarg.h> 2 #include <stdio.h> 3 4 void my_printf(const char *fmt, ...) 5 { 6 va_list ap; 7 int num_i; 8 double num_f; 9 10 va_start(ap, fmt); 11 while (*fmt) { 12 if (*fmt == ‘%‘) { 13 fmt++; 14 switch (*fmt) { 15 case ‘d‘: 16 num_i = va_arg(ap, int); 17 printf("%d", num_i); 18 break; 19 20 case ‘f‘: 21 num_f = va_arg(ap, double); 22 printf("%f", num_f); 23 break; 24 } 25 } else { 26 printf("%c", *fmt); 27 } 28 fmt++; 29 } 30 31 va_end(ap); 32 } 33 34 int main(int argc, const char *argv[]) 35 { 36 int i = 123, l = 456; 37 double j = 3.14, k = 0.618; 38 39 my_printf("i = %d j = %f k = %f l = %d ", i, j, k, l); 40 41 return 0; 42 }
第10行:va_start(ap, fmt); 可以理解为 va_start 将参数fmt之后的数据(参数中后面不确定的部分)放入一个临时栈,这个临时栈的指针为ap,指向临时栈的首地址。因为变参占用的空间大小是不确定的,因此必须找到变参中第一个参数,之后便可以根据各个参数的格式,逐一找到后面的参数。取参数的函数为:
type va_arg(va_list ap, type);
因此在第16行中调用num_i = va_arg(ap, int);表示从临时栈中取出一个int类型的数据,赋值给num_i(同时ap指针后移)同样的,第21行:num_f = va_arg(ap, double); 表示的从栈指针中找出一个double类型的变量赋值个num_f。最后使用va_end(ap)释放临时栈。
再举一个加法的例子:
1 #include <stdarg.h> 2 #include <stdio.h> 3 4 /** 5 * ote cnt表示参与加数的个数,arg1表示第一个加数 6 */ 7 int add (unsigned int cnt, int arg1, ...) 8 { 9 int i, sum = arg1; 10 va_list ap; 11 12 if (cnt == 0) 13 return 0; 14 if (cnt == 1) 15 return arg1; 16 17 va_start(ap, arg1); 18 19 for (i = 1; i < cnt; i++) { 20 sum += va_arg(ap, int); 21 } 22 23 va_end(ap); 24 25 return sum; 26 } 27 28 int main(int argc, const char *argv[]) 29 { 30 printf("%d ", add(3, 10, 20, 30)); 31 32 return 0; 33 }
add(3, 10, 20, 30)表示对3个数进行相加,分别为10,20,30,那么结果就是60。如果将17行中第二个参数改为cnt(不是已知参数中最后一个参数),结果编译的时候报警告(但是我很纳闷的是为什么输出结果仍然为60):
值得一提的是,用va_arg取出参数是先输入的参数先取出,因此变参的入栈顺序应该是最后输入的参数最先入栈,因此当可变参函数中变参部分输入的是表达式的时候,应该是先运行后面的表达式,比如下面这段程序:
1 #include <stdarg.h> 2 #include <stdio.h> 3 4 /** 5 * ote cnt表示参与加数的个数,arg1表示第一个加数 6 */ 7 int add (unsigned int cnt, int arg1, ...) 8 { 9 int i, sum = arg1; 10 va_list ap; 11 12 if (cnt == 0) 13 return 0; 14 if (cnt == 1) 15 return arg1; 16 17 va_start(ap, arg1); 18 19 for (i = 1; i < cnt; i++) { 20 sum += va_arg(ap, int); 21 } 22 23 va_end(ap); 24 25 return sum; 26 } 27 28 int main(int argc, const char *argv[]) 29 { 30 int a[3] = {10, 20, 30}; 31 int *p = &a[1]; 32 33 printf("%d ", add(3, a[0], *(--p), *(++p))); 34 35 return 0; 36 }
运行结果应该是10 + 20 + 30 = 60,而不是10 + 10 + 20。
不仅如此,我们也可以通过下面这段程序推算出定参函数参数的传入顺序(不一定入栈):
1 #include <stdio.h> 2 3 int add (int arg1, int arg2) 4 { 5 return arg1 + arg2; 6 } 7 8 int main(int argc, const char *argv[]) 9 { 10 int a[3] = {10, 20, 30}; 11 int *p = &a[1]; 12 13 printf("%d ", add(*(--p), *(++p))); 14 15 return 0; 16 }
输出结果为50而不是30。还可以通过例子说明无论是定参函数还是可变参函数,如果传入的变量是表达式,都是从最后一个参数开始计算,这里就不举例子了。
以上是关于可变参函数的主要内容,如果未能解决你的问题,请参考以下文章