C++学习笔记----10、模块、头文件及各种主题(六)---- C风格可变长度参数列表

在传统代码中,可能会碰到使用C风格变量长度的参数列表。在新的代码中,应该避免使用这些,而要用类型安全的可变长度参数列表的可变参数函数模板,关于该模板,我们以后再讨论。

这样你就知道了C风格的可变长度参数列表,考虑<cstdio>中的C函数printf()。可以使用任意数量的参数来调用它:

cpp 复制代码
import std;

using namespace std;

int main()
{
	printf("int %d\n", 5);
	printf("String %s and int %d\n", "hello", 5);
	printf("Many ints: %d, %d, %d, %d, %d\n", 1, 2, 3, 4, 5);
}

C/C++提供了语法与书写带有可变参数数量的自己的函数的一些工具宏。这些函数通常看起来很像printf()。例如,假定你想写一个应急的debug函数来打印字符串到stderr,如果debug标志被设置,如果debug标志没有设置就什么也不做。与printf()类似,该函数应该能够打印任意数量的参数与任意类型的参数的字符串。简单的实现看起来如下:

cpp 复制代码
import std;
#include <cstdarg>
#include <cstdio>

using namespace std;

bool debug{ false };

void debugOut(const char* str, ...)
{
	if (debug) {
		va_list ap;
		va_start(ap, str);
		vfprintf(stderr, str, ap);
		va_end(ap);
	}
}

代码使用了va_list(),va_start(),与va_end(),这些都是定义在<cstdarg>中的宏,因此需要显式的#include <cstdarg>,与import std;一样,不导出任何宏。类似地,stderr是一个定义在<cstdio>中的宏,需要显式的#include <cstdio>。

debugOut()的原型包含了一个类型化了的与命名的参数str,跟随的是...。代表了任意数量与类型的参数。要访问这些参数,必须声明一个va_list类型的变量并且调用va_start来初始化它。va_start()的第二个参数必须在参数列表的最右面。所有带有变长参数列表的函数要求至少一个命名参数。debugOut()函数只是将列表传递给vfprintf()(一个<cstdio>中的标准函数)。vfprintf()调用返回后,debugOut()调用va_end()来结束变量参数列表的访问。必须在调用va_start()之后调用va_end()来确保函数以可持续的状态的栈来结束。

可以用如下方式来使用该函数:

cpp 复制代码
	debug = true;
	debugOut("int %d\n", 5);
	debugOut("String %s and int %d\n", "hello", 5);
	debugOut("Many ints: %d, %d, %d, %d, %d\n", 1, 2, 3, 4, 5);

1、访问参数

如果自己想要访问真实的参数,可以使用va_arg()来达成。它接受va_list作为第一个参数,以及要解释的参数类型。不幸的是,如果不提供显式的方式来这样做,是无法知道参数列表的结束的。例如,可以使第一个参数为参数数量。或者,如果有一系列的指针,可以要求最后一个指针为nullptr。有许多方法,但对于程序员来说都是负担。

下面的例子演示了调用者指定第一个命名参数提供了多少个参数的技巧。函数接受任意数量的int并且将它们打印出来。

cpp 复制代码
void printInts(unsigned num, ...)
{
	va_list ap;
	va_start(ap, num);
	for (unsigned i{ 0 }; i < num; ++i) {
		int temp{ va_arg(ap, int) };
		print("{} ", temp);
	}
	va_end(ap);
	println("");
}

可以像下面这样调用printInts()。注意第一个参数指定了后面有多少个整数。

cpp 复制代码
printInts(5, 5, 4, 3, 2, 1);

2、为什么不应使用C风格的变长参数列表

访问C风格的变长参数列表不太安全。有几个风险,在printInts()函数中就可以看出来。

  • 不知道参数的数量。在printInts()中,必须相信调用者会传递正确的参数数量给到第一个参数。在debugOut()中,必须相信调用者传递同样数量的参数在str字符串之后,在字符串中要有替换域。
  • 不知道参数的类型。va_arg()接受一个类型,它用于解释在当前方位的值。然而,可以告诉va_arg()来解释该值为任意类型。没有办法来验证正确的类型。

警告:避免使用C风格变长参数列表。推荐传递一个std::array或vector的值,使用初始化器列表或使用类型安全的可变长度参数列表的可变参数函数模板。

相关推荐
cleveryuoyuo1 分钟前
红黑树分析
c++
yezipi耶不耶25 分钟前
Rust 所有权机制
开发语言·后端·rust
B1nna1 小时前
SpringMVC学习记录(三)之响应数据
java·学习·json·springmvc·jsp
六月悉茗3 小时前
【C语言 - 简易架构】
c语言·开发语言
qq_1873526343 小时前
c++基础34转义字符和字符函数
c++·转义字符和字符函数
風清掦3 小时前
C/C++每日一练:查找链表的中间节点
c语言·c++·链表
凡人的AI工具箱3 小时前
15分钟学 Go 第 49 天 :复杂项目开发
开发语言·人工智能·后端·算法·golang
FFDUST3 小时前
C++ 优先算法 —— 四数之和(双指针)
c语言·开发语言·c++·算法·leetcode·1024程序员节
桃酥4033 小时前
day13|C++重难点之 静态变量、全局变量、局部变量的区别,在内存上是怎么分布的、指针和引用的区别、C++内存分区
c++·指针·引用·内存分区·静态变量
小周不摆烂4 小时前
Java基础-内部类与异常处理
java·开发语言