Fork me on GitHub

可变参数函数

可变参数函数概述

在c++的学习和编码过程中,会遇到某些函数的参数是不确定的;其中最常见的就是printf函数,其原型为
int printf( const char* format, ...);
它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的(用三个点“…”做参数占位符),实际调用时可以有以下的形式:
printf("%d",i);
printf("%s",s);
printf("the number is %d ,string is:%s", i, s);

简单的例子

下面我们通过2个简单的可变参数的C函数来具体分析:

用最后一个固定参数来确定可变参数的个数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include "stdarg.h"
void simple_va_fun(int num, ...)
{
va_list arg_ptr;//这里写的什么list,(搞得好像是得到可变参数列表头一样)其实它就是个字符指针:char *
int nArgValue = 0;
va_start(arg_ptr,num); //以最后一个固定参数num的地址为起点确定变参的内存起始地址。
printf("num:%d, *arg_ptr:%d\n", num, *arg_ptr);// 这里打印下就会看出,*arg_ptr 跳过了num指向了下一个参数
for (int i = 0; i < num; i++)
{
nArgValue = va_arg(arg_ptr,int); //得到下一个可变参数的值
printf("the %d th arg_ptr: %d \n",i,nArgValue); //输出各参数的值
}

va_end(arg_ptr);//结束标志
return;
}

int main(int argc, char* argv[])
{
simple_va_fun(2, 200, 201);
return 0;
}

输出结果为:
num:2, *arg_ptr:200
the 0 th arg: 200
the 1 th arg: 201

最后一个参数用负数作为结束标志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include "stdarg.h"
int test(int tmp, ...){

va_list arg_ptr;
va_start(arg_ptr, tmp);
while(tmp != -1){
tmp = va_arg(arg_ptr, int);
printf("the %d th arg_ptr: %d \n",*arg_ptr,tmp);
}
va_end(arg_ptr);
return 0;
}

int main(int argc, char* argv[])
{
int a = 100, i = 1, j = 2, k = 3, g = -1;

printf("test1:\n");
test(a, i, j, k, g);

printf("test2:\n");
a = 200, i = 11, j = 12, k = 13;
test(a, i, j, g);

return 0;
}

输出结果如下:
test1:
the 1 th arg_ptr: 1
the 2 th arg_ptr: 2
the 3 th arg_ptr: 3
the -1 th arg_ptr: -1
test2:
the 11 th arg_ptr: 11
the 12 th arg_ptr: 12
the -1 th arg_ptr: -1

这个和上面是一样的,唯一不同的是可变参数列表的第一个参数,没有用来当作参数个数,而是把最后一个参数用负数作为结束标志,参数列表第一个参数在这里的作用仅仅是为了得到可变参数列表的起始地址

分析

可变参数列表的实现实际上是由几个宏定义组成的,在文件include/stdarg.h

  • var_list用于定义某个变量,其实质是typedef char *va_list;//字符指针类型
  • va_start(ap, tmp)表示开始获取可变参数列表中的第一个参数(…里面的第一个),跳过最后一个固定参数tmp
  • va_arg(ar, type)表示循环获取到可变参数列表中的参数,args指向下一个参数地址,返回的则是当前参数地址,type表示参数类型
  • var_end(ap)是结束标志

由此可见,当需求中,参数个数无法确定的时候,可以使用可变参数函数,是很方便的。
但是我们可以发现,以上所讲的例子🌰上还是有一定的缺陷的,最起码参数的类型就被限定死了,那么或许有人会问,printf函数不就是实现了能够智能识别参数类型的吗?那是因为函数printf是从固定参数format字符串来分析出参数的类型,再调用va_arg的来获取可变参数的。也就是说,你想实现智能识别可变参数的话是要通过在自己的程序里作判断来实现的。

函数变形(智能识别参数类型)

综上,可以变形得到下面的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>
#include "stdarg.h"
void simple_va_fun(char *fmt, ...)
{
va_list arg_ptr;
int d;
char c, *p, *s;
va_start(arg_ptr, fmt); //以固定参数的地址为起点确定变参的内存起始地址。
while (*fmt)
{
if ('%' == *fmt) {
switch (*(++fmt)) {
case 's': /* string */
s = va_arg(arg_ptr, char *);
printf("-----%s\n", s);
break;
case 'd': /* int */
d = va_arg(arg_ptr, int);
printf("-----%d\n", d);
break;
case 'c': /* char */
/* need a cast here since va_arg only takes fully promoted types */
c = (char)va_arg(arg_ptr, int);
printf("-----%c\n", c);
break;
default:
c = *fmt;
printf("-default---%c\n", c);
} // end of switch
}
else {
c = *fmt;
printf("-else---%c \n ", c);
}
++fmt;
}
va_end(arg_ptr);
}

int main(int argc, char* argv[])
{
simple_va_fun("%s, %d, %c", "testStr", 854, 'k');

return 0;
}

输出结果为:
-----testStr
-else---,
-else---
-----854
-else---,
-else---
-----k

这里采用类似printf函数的方法,在最后一个固定参数传入可变参数的类型,在运行时智能识别参数类型,并获取到参数的值

Enjoy it ? Donate for it ! 欣赏此文?求鼓励,求支持!
>