在 C/C++ 中,可变参数列表以省略号运算符 ...
表示。它具有许多用途,具体取决于具体的使用场景。 最初在 C 中用作可变函数参数列表的抽象声明符;在 C++ 中,可用于异常处理 catch 块中;而在 C++11 中,则用于可变参数模板。
printf("Hello, World! \n");
说起可变参数,printf() 可以说是最经典的应用之一。
int printf(
const char *format [,
argument]...
);
从接触 C/C++ 开始,‘printf()’ 们可谓如影随形。在实际的应用中,界面数据的格式化输出、日志的格式化输出,这些都和可变参数密不可分。
可变参数列表的格式化,主要有两种方式:
1. 可变参数列表va_list和vsprintf
2. 可变参数模板和sprintf (C++11)
接下来,以 format_string() 函数为例进行说明。以下是主体部分:
#include
#include
#include
void format_string(const char* format, ...)
{
// TODO:...
}
int main()
{
format_string("%s, %s", "hello", "this is a string.");
return 0;
}
1. 可变参数列表va_list和vsprintf
1.1 va_start、va_end、vsprintf 的使用
#define MAX_BUFFER 128
void format_string(const char* format, ...)
{
char buffer[MAX_BUFFER] = {
0 };
va_list args;
va_start(args, format);
vsprintf(buffer, format, args); // warning C4996: 'vsprintf': This function or variable may be unsafe. Consider using vsprintf_s instead.
va_end(args);
puts(buffer);
}
输出:hello, this is a string.(加上结束符’\0’,共25个字符)
以上便是基于 va_list 的可变参数格式化实现。args 是指向参数列表的指针,通过 va_start 将 args 设置为传递给函数的参数列表中的第一个可选参数,然后使用 vsprintf 进行参数检索和格式化输出。
有没有很眼熟?vsprintf 比我们常用的 sprintf 前面多一个v,表示是用于可变参数列表(variable-argument list) 的。它们的区别在于,sprintf 的入参是可变参数列表(省略号),而 vsprintf 的入参是可变参数列表指针。
1.2 “warning C4996”的前因后果
在编译过程中,会有这样一个警告:warning C4996: 'vsprintf': This function or variable may be unsafe. Consider using vsprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS.
也可能是 error C4996, 这取决于VS项目属性配置中 “C/C++” 下是否开启了SDL检查。
通常情况下,我们会选择忽略或屏蔽这个警告。一般情况下是可以这样操作,但是会存在潜在问题,后面会进行说明。出现C4996
的提示,是由于微软为了增强安全机制,弃用了部分C运行时库(CRT)函数,以_s
结尾的同名函数替代。微软认为,这些函数是安全错误的常见来源,因为它们不会阻止覆盖内存的操作。这个怎么理解呢?接下来看看错误是如何出现的。
首先,将输出缓冲区 MAX_BUFFER 大小调整为 20,看看会发生什么。
#define MAX_BUFFER 20
void format_string(const char* format, ...)
{