可变参数函数的实现和用途
可变参数函数是指在函数定义时,允许指定不定数量的参数,其实现方式一般是使用 C99 中的 stdarg.h 头文件或者 C++11 中的可变参数模板。可变参数函数可以用于处理数量不定的数据,能够极大地提高程序的灵活性和适用性。下面将详细介绍可变参数函数的实现和用途。
一、可变参数函数的实现
C99 中的 stdarg.h 头文件提供了一组可变参数函数的宏,包括 va_list、va_start、va_arg 和 va_end。其中 va_list 是指向参数列表的指针,由宏 va_start 初始化,va_arg 获取参数的值,va_end 在函数结束时销毁 va_list。下面是一个求平均值的可变参数函数的示例:
#include <stdarg.h>
#include <stdio.h>
double average(int num, ...) {
va_list valist;
double sum = 0.0;
int i;
va_start(valist, num);
for (i = 0; i < num; i++) {
sum += va_arg(valist, double);
}
va_end(valist);
return sum / num;
}
int main() {
printf("Average of 2, 3, 4, 5, 6 = %f
", average(5, 2.0, 3.0, 4.0, 5.0, 6.0));
printf("Average of 3, 2, 1 = %f
", average(3, 3.0, 2.0, 1.0));
return 0;
}
在例子中,average 函数的 个参数 num 表示传递给函数的参数个数,后面的 ... 表示可变参数列表。va_list valist 定义了一个指向可变参数列表的指针,在 va_start 宏中初始化。va_arg 宏获取可变参数列表中的参数值,并且根据参数类型向后移位。最后,用 va_end 宏销毁 valist。
由于可变参数函数调用时传递的参数数量不定,因此在使用时需特别注意类型与数量的匹配。
除了使用 stdarg.h 头文件外,C++11 还提供了可变参数模板,使用方法如下:
#include <iostream>
#include <cstdlib>
#include <cstdarg>
double average(int num, ...) {
va_list valist;
double sum = 0.0;
int i;
va_start(valist, num);
for (i = 0; i < num; i++) {
sum += va_arg(valist, double);
}
va_end(valist);
return sum / num;
}
template<typename ...Args>
void print(Args ...args) {
std::cout << sizeof...(args) << std::endl;
}
int main() {
std::cout << "Average of 2, 3, 4, 5, 6 = " << average(5, 2.0, 3.0, 4.0, 5.0, 6.0) << std::endl;
std::cout << "Average of 3, 2, 1 = " << average(3, 3.0, 2.0, 1.0) << std::endl;
print(1, 2, 3, "4", "5");
return EXIT_SUCCESS;
}
这里使用了模板函数 print,其中 sizeof...(args) 表示参数包的大小。可以看出,可变参数模板的语法更加简单直观,并且能够进一步提高程序的可读性和可维护性。
二、可变参数函数的用途
可变参数函数广泛应用于打印、日志、字符串格式化等场景,可以便捷地格式化输出任意数量的参数。例如,在 C++ 中,可以定义一个 log 函数,用于打印日志:
#include <iostream>
#include <cstdlib>
#include <cstdarg>
void log(const char * format, ...) {
va_list valist;
va_start(valist, format);
vfprintf(stderr, format, valist);
va_end(valist);
}
int main() {
log("Error: %s %d
", "file not found", 404);
return EXIT_SUCCESS;
}
在 log 函数中,format 参数表示格式化字符串,... 表示可变参数列表。使用 vfprintf 函数可以根据格式化字符串和可变参数,格式化输出日志信息。
除了常规的打印和日志功能,可变参数函数还可以用于格式化字符串、处理 CSV 文件等各种场景,具有很强的通用性和适用性。例如,对于存在标题行的 CSV 文件,可以使用如下代码解析数据:
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstdarg>
#include <sstream>
std::vector<std::vector<std::string>> read_csv(const char *filename, bool header=true, char sep=',') {
std::vector<std::vector<std::string>> data;
std::ifstream file(filename);
std::string line;
int rowno = 0;
while (std::getline(file, line)) {
std::vector<std::string> vec;
std::stringstream ss(line);
std::string field;
int colno = 0;
while (std::getline(ss, field, sep)) {
vec.push_back(field);
colno++;
}
if (header && rowno == 0) {
// ignore header
} else {
data.push_back(vec);
}
rowno++;
}
file.close();
return data;
}
int main() {
auto data = read_csv("/path/to/data.csv", true, ',');
for (auto row : data) {
for (auto field : row) {
std::cout << field << " ";
}
std::cout << std::endl;
}
return EXIT_SUCCESS;
}
在 read_csv 函数中,filename 表示文件名,header 和 sep 分别表示是否包含标题行和字段分隔符,默认分隔符为逗号。内部使用 std::stringstream 进行字符串转换,从而能够处理任意数量的字段。如果包含标题行,则在 行时忽略,否则直接添加到 data 中。最后返回数据向量。
三、总结
可变参数函数是实现可变参数特性的重要工具,可以处理任意数量的参数,适用于处理打印、日志、字符串格式化、CSV 等场景。需要注意的是,在使用时需要特别关注参数类型和数量的匹配,以免出现不可预见的错误。在 C++ 11 中增加了可变参数模板的支持,使得可变参数函数的实现更加简单和优雅,具有很强的可读性和可维护性。
