c(++)变长参数之整形(非字符串类型类似)
0、序言
变长参数,接触的第一个可变长参数函数是 printf , 然后是 scanf 。他们的原型如下:
printf:
_Check_return_opt_ _CRT_STDIO_INLINE int __CRTDECL printf( _In_z_ _Printf_format_string_ char const* const _Format, ...)
scanf
_CRT_STDIO_INLINE int __CRTDECL scanf( _In_z_ _Scanf_format_string_ char const* const _Format, ...)
本文演示的是数据类型是 int .
1、使用
A、头文件
// 使用va_start需要的头文件 #include <stdarg.h>
B、使用必须使用的4个宏,原型就不贴出来了,大家更关心的是怎么使用。:
va_list :保存可变长参数
va_start:构建一个参数集合
va_arg:获取集合中的参数
va_end:释放集合
C、和普通参数使用相似,第一个参数必须要指定为类型,后面 写三个 点。例如:
vodi add(const int param, ...);
2、一个完整的例子
演示环境: VS2015 up3
这里,定义了一个函数,函数用来求和,参数类型为整形,参数为可变长参数
1 #include <iostream> 2 3 // 使用va_start需要的头文件 4 #include <stdarg.h> 5 6 // 不定长参数求和 7 void va_sum(const int first_param, ...) 8 { 9 10 va_list ap; 11 12 va_start(ap, first_param); 13 14 // 保存求和的结果, 演示不定长参数用法,暂时不考虑越界 15 int sum = 0; 16 17 // 中间每一项 18 int tmp = 0; 19 20 // -1 表示 集合的结束 21 for (int i = 0; -1 != tmp ; i++) 22 { 23 // 显示第i个参数 24 std::cout << "i=" << i << ", tmp=" << tmp << "\n"; 25 // 保存结果 26 sum += tmp; 27 // 找到下一个参数 28 tmp = va_arg(ap, int); 29 30 } 31 32 // 释放空间 33 va_end(ap); 34 35 // 输出结果 36 std::cout << "sum = " << sum << "\n"; 37 } 38 39 // 入口函数 40 int main(int argc, char *argv[]) 41 { 42 // -1 表示可变长参数的结束 43 va_sum(1, 2, 3, 4, -1); 44 45 system("pause"); 46 return 0; 47 }
这段代码,大家先看下, 预测下 sum 的输出结果。 下面将有正确答案。
我第一次作答的答案: 函数 va_sum 用来求和的, main函数传入了参数: 1. 2. 3. 4. -1. 所以, va_sum中的sum=1 + 2 + 3 + 4 = 10。 sum将输出 10。
正确答案是:

sum 怎么不是10 ? 怎么i= 0,输出的不是第一个参数1, 而是 2?
踩坑:检查发现, sum 少加了 参数 1。 阅读上面的代码,不难发现, 当 第一次 调用 va_arg 宏时, 此时得到的是集合的第1个参数,而不是调用传入的参数1. 1 是第一个参数。
具体,咱们看下 va_start, va_end, va_arg的宏定义(来自Vs2015up3中vadefs.h文件的定义)
1 #define __crt_va_start_a(ap, v) ((void)(ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v))) 2 #define __crt_va_arg(ap, t) (*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))) 3 #define __crt_va_end(ap) ((void)(ap = (va_list)0))
明白了。
怎么改实现求和? sum 加上 第一个参数 :first_param 即可。va_sum 函数的for循环前增加一行代码:
// 新增加 sum += first_param;
va_sum函数完整定义:
1 // 不定长参数求和 2 void va_sum(const int first_param, ...) 3 { 4 5 va_list ap; 6 7 va_start(ap, first_param); 8 9 // 保存求和的结果, 演示不定长参数用法,暂时不考虑越界 10 int sum = 0; 11 12 // 中间每一项 13 int tmp = 0; 14 15 // 新增加 16 sum += first_param; 17 18 // -1 表示 集合的结束 19 for (int i = 0; -1 != tmp ; i++) 20 { 21 // 显示第i个参数 22 std::cout << "i=" << i << ", tmp=" << tmp << "\n"; 23 // 保存结果 24 sum += tmp; 25 // 找到下一个参数 26 tmp = va_arg(ap, int); 27 28 } 29 30 // 释放空间 31 va_end(ap); 32 33 // 输出结果 34 std::cout << "sum = " << sum << "\n"; 35 }
输出结果:

3、总结
A、第一次调用 va_arg 指向的是集合的第一个元素,而不是调用时传入的第一个参数。
B、va_list 保存的时 函数参数 三个点【...】的参数,调用时传入的第一个参数保存在函数的第一个参数中。
C、 va_start 与 va_end 需要配对使用。
D、需要指定结束符。 这里 -1 为结束符。

浙公网安备 33010602011771号