有时候,我们写函数的时候,可能遇到参数数量不确定的情况,这样的函数例如C的printf打印函数。你可以使用printf("some string"), 也可以使用printf("%d", aintvalue)。是不是C的库里包含了不同定义的printf函数呢?答案肯定是否定的,因为C是不支持函数的重载的。每个函数只能由一个版本。那么是如何实现类似这种可变参数传递的函数呢?在这讲三种方法。
(1)方法1:使用va_list
涉及到如下变量和函数(摘自msdn):
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );
void va_start( va_list arg_ptr ); (UNIX version)
void va_start( va_list arg_ptr, prev_param ); (ANSI version)
参数含义:
type:参数类型
arg_ptr:指向参数列表的指针
prev_param:第一个参数的类型
下面采用msdn里的一个例子。
1. /* VA.C: The program below illustrates passing a variable
2. * number of arguments using the following macros:
3. * va_start va_arg va_end
4. * va_list va_dcl (UNIX only)
5. */
6.
7. #include <stdio.h>
8. #define ANSI /* Comment out for UNIX version */
9. #ifdef ANSI /* ANSI compatible version */
10. #include <stdarg.h>
11. int average( int first, ... );
12. #else /* UNIX compatible version */
13. #include <varargs.h>
14. int average( va_list );
15. #endif
16.
17. void main( void )
18. {
19. /* Call with 3 integers (-1 is used as terminator). */
20. printf( "Average is: %d\n", average( 2, 3, 4, -1 ) );
21.
22. /* Call with 4 integers. */
23. printf( "Average is: %d\n", average( 5, 7, 9, 11, -1 ) );
24.
25. /* Call with just -1 terminator. */
26. printf( "Average is: %d\n", average( -1 ) );
27. }
28.
29. /* Returns the average of a variable list of integers. */
30. #ifdef ANSI /* ANSI compatible version */
31. int average( int first, ... )
32. {
33. int count = 0, sum = 0, i = first;
34. va_list marker;
35.
36. va_start( marker, first ); /* Initialize variable arguments. */
37. while( i != -1 )
38. {
39. sum += i;
40. count++;
41. i = va_arg( marker, int);
42. }
43. va_end( marker ); /* Reset variable arguments. */
44. return( sum ? (sum / count) : 0 );
45. }
46. #else /* UNIX compatible version must use old-style definition. */
47. int average( va_alist )
48. va_dcl
49. {
50. int i, count, sum;
51. va_list marker;
52.
53. va_start( marker ); /* Initialize variable arguments. */
54. for( sum = count = 0; (i = va_arg( marker, int)) != -1; count++ )
55. sum += i;
56. va_end( marker ); /* Reset variable arguments. */
57. return( sum ? (sum / count) : 0 );
58. }
59. #endif
通过上面的例子可以清楚知道怎么传递可变参数了。
(2)方法2:利用参数入栈原理
在现有的32位的机器上,一般通过将参数按照由低地址到高地址的顺序依次入栈实现传递,因此把第一参数的指针依次增加,就可以得到后面的参数了。例如:
1. struct s1
2. {
3. int a;
4. char b;
5. };
6.
7. struct s2
8. {
9. int a;
10. };
11.
12. void function (struct s1 a, ...)
13. {
14. struct s1* pa = &a;
15. printf("%d, %c\n", pa->a, pa->b);
16. ++pa;
17. printf("%d, %c\n", pa->a, pa->b);
18. ++pa;
19. printf("%d\n", (struct s2*)pa->a); //强制转换为struct s2类型
20. }
21. void main( void )
22. {
23. struct s1 a1 = {1, 'A'};
24. struct s1 a2 = {2, 'B'};
25. struct s2 a3 = {10};
26. function(a1, a2, a3); //a3的类型不同于a1, a2
27. return;
28. }
29.
用这种方法传递可变参数非常简单。
(3)方法3:用数组传参
1. void function(Type a[])
2. {
3. ...
4. a[0];
5. a[1];
6. ...
7. }
这种方法就不唠叨了,相信地球人都知道。