可变参数 = 参数个数不固定:定义里写 ...(省略号),表示"后面还有若干个参数,来多少收多少"。取出它们靠 <cstdarg> 的四个工具:va_list / va_start / va_arg / va_end。最大的坑——函数自己数不出有几个参数,必须由调用方额外告诉它。
6.2.3 的默认参数解决"少传",这一节的省略号解决"多传"——两者合起来,就是把 6.2.2 那条"个数必须匹配"的规矩补出了两个例外。其实你早就用过可变参数了:2.5.3 见过的 printf("%d %s", a, b) 就是它最著名的用户。
一、写法
返回类型 函数名(固定参数表, ...)
注意两点:
1. ... 前面至少要有一个固定参数——不能写成 f(...),那个固定参数是"最后一块路标"。
2. ... 只是告诉编译器"这里开始不管了",具体有几个、都是什么类型,编译器一概不问(这正是它不安全的原因)。
二、四个工具(都在 <cstdarg> 里)
| 工具 | 干什么 | 比喻 |
|---|---|---|
va_list ap; |
声明一个"取货清单" | 拿着清单准备取货 |
va_start(ap, 最后一个固定参数); |
把清单定位到第一个可变参数 | 站到货架起点 |
va_arg(ap, 类型); |
取出一个,并声明它是什么类型 | 拿一件、顺手报一下是什么 |
va_end(ap); |
用完收工 | 清单作废、柜台还原 |
顺序钉死:start → arg(可多次)→ end。
三、实例 03:定义可变参数函数
#include <iostream>
#include <cstdarg> // 可变参数的四件套都在这个头文件里
using namespace std;
int sum(int count, ...) { // count:告诉函数"后面有几个数"
va_list ap; // ① 取货清单
va_start(ap, count); // ② 站到起点(从 count 后面开始)
int total = 0;
for (int i = 0; i < count; i++) {
total += va_arg(ap, int); // ③ 逐个取出,每次都要说清楚类型
}
va_end(ap); // ④ 收工
return total;
}
int main() {
cout << "两数之和: " << sum(2, 10, 20) << endl;
cout << "三数之和: " << sum(3, 1, 2, 3) << endl;
cout << "五数之和: " << sum(5, 1, 2, 3, 4, 5) << endl;
return 0;
}
看到什么算成功:
两数之和: 30
三数之和: 6
五数之和: 15
关键就在第一个参数 count:函数必须靠它知道要取几次。
- 写成 sum(2, 10, 20, 30) → 只取两个,后面的 30 被无视(悄悄消失)
- 写成 sum(5, 1, 2) → 去取不存在的东西(越界读取),结果乱七八糟甚至崩溃
这就是可变参数的原罪:没有长度信息,全靠调用方自觉。
30 秒实验:先正确调 sum(3, 5, 5, 5)(得 15),再故意调 sum(5, 5, 5, 5)(只给 4 个数却说 5 个)——输出很可能是垃圾值或程序直接崩。用身体记住"越界读"的后果(和第五章数组越界是同一类事故,第 7 章会正式讲)。
四、printf 就是可变参数函数
printf("%d 加 %d 等于 %d\n", 3, 5, 8);
| 部分 | 角色 |
|---|---|
"%d 加 %d 等于 %d\n" |
唯一固定参数(格式串) |
3, 5, 8 |
可变部分 |
格式串里的三个 %d |
"有几个、什么类型"的说明书 |
printf 正是靠格式串里的 % 个数决定取几次、靠 %d/%s 决定按什么类型取。这就解释了 2.5.3 那个坑:格式串写错(少写一个 %d 却多给一个参数,或者类型对不上),输出就是乱码——因为它是"照着自己说的去取",取错了也不会有人拦。
五、常见误区
- "可变参数可以没有固定参数"——不能,
...前面至少要有一个。 - "函数能自己数出有几个参数"——完全不能。这是它最根本的缺陷:要么让调用方传个数(
count),要么用格式串说明(printf)。 - "类型可以随便指"——
va_arg(ap, int)的类型必须和实际传的严格一致。传了double却按int取,就是垃圾值,编译器不会帮你检查(可变参数完全没有类型检查)。 - "可变参数是最方便的传参方式"——相反,它不安全、难调试、容易越界。教材让你认识它,是为了看懂
printf/scanf这类老函数;你自己写新函数时,优先考虑"数组 + 个数"(第 7 章)——安全得多。
自测三问(点开对答案)
1. 可变参数的四个工具是什么?——`va_list`、`va_start`、`va_arg`、`va_end`。 2. 函数怎么知道有几个可变参数?——**不知道**,必须由调用方额外说明(`count` 参数或格式串)。 3. `printf` 的格式串起什么作用?——告诉它有几个参数、各是什么类型(少写或多写都会导致取错)。- ☐ 6.2.5 返回值(下一节)
相关笔记
- 默认参数 —— 另一个"个数例外"
- 流输出格式控制 —— printf 的另一面
- 数据类型 ——
va_arg里必须报的类型 - 🔷 C++基础 · 索引 —— 全课路线图