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 为结束符。

 

posted @ 2020-09-07 21:28  mohist  阅读(358)  评论(0)    收藏  举报