从 scanf() 和 printf() 讲起

在使用 scanf()printf() 时,你会发现一个奇怪的现象,它的参数个数是不确定的,格式化字符串中有多少个%,后面就要跟多少个参数

1
2
3
4
printf("%d",a);
printf("%d %d",a,b);
printf("%d %d %d",a,b,c);
......

但在我们一般的印象中,一个函数在被声明时,它的参数个数就固定了,不能改变

让我们在 stdio.h 中找到 scanf() 的定义:

1
2
3
4
5
6
7
8
int scanf(const char *__format, ...)
{
int __retval;
__builtin_va_list __local_argv; __builtin_va_start( __local_argv, __format );
__retval = __mingw_vfscanf( stdin, __format, __local_argv );
__builtin_va_end( __local_argv );
return __retval;
}

接受参数怎么有个省略号?还可以这样写吗?

这便牵扯到可变参数

可变参数的头文件

我们在使用可变参数时,需要引入 stdarg.h 头文件

stdarg.h 中,有一个数据类型,四个宏

类型 描述
va_list 用来保存宏 va_arg 与宏 va_end 所需信息
描述
va_start(va_list,省略号的前一个参数) 使 va_list 指向起始的参数
va_arg(va_list,参数类型) 检索参数并返回
va_end(va_list) 释放 va_list 的内存空间
va_copy(va_list, va_list) 拷贝 va_list 的内容,va_copy(va2, va1) 作用为拷贝 va1到 va2

如何使用

  1. 定义一个函数,最后一个参数为省略号,省略号前面可以有任意个参数,但至少要有一个
  2. 声明一个 va_list 类型变量用于承载参数列表
  3. 使用 va_start() 初始化 va_list
  4. 多次使用 va_arg() 依次获取参数列表里的值,每执行一次就返回一个参数(通过类型自动跳到下一个参数的位置)
  5. 使用宏 va_end() 来回收 va_list 变量

注意:没有机制可以确定省略号里一共传递了多少个参数,以及他们各自的数据类型( va_arg() 会一直往后跳,即使已经超出了总参数的个数,它也不知道)

为了解决这个问题,通常有三种方法

  • 在省略号前传入一个整型,表示后面参数的总数(总数变量法)
  • 使用一个特定的标识符来确定参数的结尾,如传递一串 int ,并使用 -1 表示数据结束(标识符法)
  • 在省略号前传入一个格式化字符串,通过字符串确定参数的数量以及各自的类型(格式化字符串法,类似 scanf()printf()

样例

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
#include <stdio.h>
#include <stdarg.h>

int sum(int num, ...)
{
int sum = 0;
va_list list; //定义 va_list 类型变量

va_start(list, num); //初始化

for (int i = 1; i <= num; i++)
sum += va_arg(list, int); //依次获取参数

va_end(list); //回收变量

return sum;
}

int main()
{
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);

printf("Sum of 2, 3, 4, 5 = %d\n", sum(4, 2, 3, 4, 5)); // Sum of 2, 3, 4, 5 = 14
}
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
#include <stdio.h>
#include <stdarg.h>

int sum(int tmp, ...)
{
int sum = 0;
va_list list; //定义 va_list 类型变量

va_start(list, tmp); //初始化

for (int i = tmp; i != -1; i = va_arg(list, int))
sum += i; //依次获取参数

va_end(list); //回收变量

return sum;
}

int main()
{
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);

printf("Sum of 2, 3, 4, 5 = %d\n", sum(4, 2, 3, 4, 5, -1)); // Sum of 2, 3, 4, 5 = 14
}

不想写,评论区有大佬的话可以写一个

参考资料: