C语言不定参函数
函数用法总结
Va_list
- 作用:类型定义,生命一个变量,该变量被用来访问传递给不定参函数的可变参数列表
- 用法:供后续函数进调用,通过该变量访问参数列表
typedefchar* va_list;
va_start
- 作用:初始化va_list变量,指向可变参数列表中的第一个参数,这个宏需要知道最后一个固定参数的位置,方便从其后开始读取可变参数
- 参数:ap(初始化的va_list变量)、last_artg(确定从哪里开始读取可变参数4)
voidva_start(va_list ap, last_arg);
va_arg
- 作用:va_arg宏用于获取可变参数列表中的下一个参数,并将其转换为指定的类型
- 参数:ap,type指定要获取的参数类型
- 返回值:返回指定类型的下一个参数值,并将va_list 变量指向下一个参数
type va_arg(va_list ap, type);
va_end
- 作用:用于清理va_list变量
- 参数:ap需要清理的va_list变量
voidva_end(va_list ap);
使用事例
使用事例
- 至少定义一个固定参数的函数,然后后面的参数使用... 表示
- va_list args:定义一个变量,用于存储不定参数的列表
- va_start(args, fixeArg):初始化args,并让其指向可变参数列表的开始;fixeArg则是最后一个固定参数,用于确定从哪里开始寻找可变参数
- va_arg(args,int):获取当前参数,类型设置为int,并将指针移动到下一个参数
- va_end(args):清理va_list,结束不定参数的获取
#include<stdarg.h>
#include <stdio.h>
// 定义一个不定参函数
void exampleFunction(int fixedArg, ...)
{
va_list args;
va_start(args, fixedArg);
// 获取不定参
int nextArg;
while ((nextArg = va_arg(args, int)) != 0)
{
printf("Argument: %d\n", nextArg);
}
va_end(args);
}
int main()
{
exampleFunction(1, 2, 3, 4, 5, 0); // 0作为终止条件
return 0;
}
#include<iostream>
#include <cstdarg>
void printNum(int n, ...)
{
va_list al;
va_start(al, n); // 让al指向n参数之后的第一个可变参数
for (int i = 0; i < n; i++)
{
int num = va_arg(al, int); // 从可变参数中取出一个整形参数
std::cout << num << std::endl;
}
va_end(al); // 清空可变参数列表--其实是将al置空
}
int main() {
printNum(3, 11, 22, 33); // 输出:11 22 33
printNum(5, 44, 55, 66, 77, 88); // 输出:44 55 66 77 88
return 0;
}
vasprintf函数
格式化输出写入一个动态分配字符串
- char **strp:指向字符指针的指针,用于接收分配的内存的地址,
vasprintf
会为存储格式化后的字符串动态分配内存,并将其地址存储在*strp
中。调用者需要负责在使用完字符串后使用free()
函数释放这块内存- const char *fmt:这是格式化字符串,类似于
printf
函数中的格式化字符串。它可以包含格式说明符(如%d
,%s
等),并且需要与后续的可变参数相匹配- va_list ap:这是一个
va_list
类型的变量,用于传递可变参数列表。它通常通过va_start
宏初始化返回值
- 成功:返回生成字符串的长度
- 失败:返回-1,同时将*strp设置成NULL
intvasprintf(char **strp, constchar *fmt, va_list ap);
- 函数create_message:初始化变量ap,从fmt之后的参数开始获取可变参数
- vasprintf:根据fmt中的格式化字符串,以及ap生成最终字符串,动态分配内存存储生成的字符串,并将其存储在strp中(将格式化字符格式和可变参数结合,生成固定格式的消息,存储在strp中)
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
int create_message(char **strp, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
int result = vasprintf(strp, fmt, ap);
va_end(ap);
return result;
}
int main() {
char *message;
int length = create_message(&message, "Hello, %s! You have %d new messages.", "Alice", 5);
if (length != -1) {
printf("Generated string: %s\n", message);
printf("String length: %d\n", length);
free(message); // 记得释放分配的内存
} else {
fprintf(stderr, "Memory allocation failed\n");
}
return 0;
}
//
Generated string: Hello, Alice! You have 5 new messages.
String length: 34
C++风格不定参
不定参宏实现日志功能
- 宏定义:#define Log( fmt , ...):定义一个Log宏,该宏接受一个格式化字符串fmt,以及一个可变数量的附加参数
- __FILE__和 __LINE__:预定义宏,当前源文件的文件名和行号
- ##__VA_ARGS:宏的可变参数部分为空的时候,可以自动取出前面的逗号,避免语法错误
#include<iostream>
#include <cstdarg>
#define LOG(fmt, ...) printf("[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
int main()
{
LOG("%s-%s", "hello", "世界");
LOG("%s-%s", "hello", "中国");
return 0;
}
实现C风格的不定参函数
基本与C语言风格相同
#include<iostream>
#include <cstdarg>
void printNumbers(int count, ...)
{
va_list args;
va_start(args, count); // 初始化 va_list 并将其指向 count 之后的第一个可变参数
for (int i = 0; i < count; ++i)
{
int num = va_arg(args, int); // 逐个获取参数
std::cout << num << " ";
}
va_end(args); // 清理 va_list
std::cout << std::endl;
}
int main() {
printNumbers(3, 10, 20, 30); // 输出: 10 20 30
printNumbers(5, 1, 2, 3, 4, 5); // 输出: 1 2 3 4 5
return 0;
}
C++11可变模版
通过递归模版展开
#include<iostream>
void print() {
std::cout << std::endl; // 递归基例:什么都不做,只是结束递归
}
template<typename T, typename... Args>
void print(T first, Args... args) {
std::cout << first << " "; // 打印第一个参数
print(args...); // 递归调用 print 处理剩余参数
}
int main() {
print(1, 2.5, "Hello", 'c'); // 输出: 1 2.5 Hello c
return 0;
}
C++17折叠表达式
#include<iostream>
template<typename... Args>
void print(Args... args) {
(std::cout << ... << args) << std::endl; // 使用折叠表达式展开参数包
}
int main() {
print(1, 2.5, "Hello", 'c'); // 输出: 1 2.5 Hello c
return 0;
}
ofstream
专门用于文件输出操作,就是将数据写入文件。fstream
是 std::basic_ofstream
的一个实例化,std::basic_ofstream
是一个模板类,用于处理各种字符类型的文件输出。ofstream
专门处理 char
类型的文件
基本使用总结
文件打开关闭
- ofstream对象可以利用自身构造或者open方法打开文件,文件只要被打开,程序就可以将数据写入文件
- 程序结束或者该对象销毁的到时候,文件会自动关闭,也可以手动关闭文件,即调用close()方法
std::ofstream file("example.txt");
if (file.is_open()) {
// 文件已成功打开,可以进行写入操作
}
写入数据
- 支持向文件写入多种类型的数据
std::ofstream file("example.txt");
if (file.is_open()) {
file << "Hello, World!" << std::endl;
file << 123 << std::endl;
file << 45.67 << std::endl;
}
检查文件状态
- is_open():检查文件是否成功打开
- good():检查文件流是否处于有效状态
- fail:检查是否出现了某种错误
std::ofstream file("example.txt");
if (!file) {
std::cerr << "Failed to open the file." << std::endl;
}
文件模式
std::ios::out
:默认模式,表示文件用于写入。std::ios::app
:追加模式,在文件末尾添加内容而不覆盖已有内容。std::ios::trunc
:清空文件内容(默认行为,如果文件已存在)。std::ios::binary
:以二进制模式打开文件,而非文本模式
std::ofstream file("example.txt", std::ios::app);
自动资源管理
- 内部是RAII模式管理文件资源,只要该对象超出作用域,文件就会自动关闭
{
std::ofstream file("example.txt");
file << "This will be written to the file." << std::endl;
} // file 对象超出作用域,文件自动关闭
ifstream
标准库中的一个类,属于 <fstream>
头文件,它用于文件输入操作,即从文件中读取数据。ifstream
是 std::basic_ifstream
的一个实例化,std::basic_ifstream
是一个模板类,用于处理各种字符类型的文件输入。ifstream
专门处理 char
类型的文件
基本使用总结
文件打开和关闭,与Ofstream相同,构造函数打开,打开后程序可以从文件中读取数据,程序结束或者对象被销毁的时候,文件自动关闭。手动关闭就是close()
std::ifstream file("example.txt");
if (file.is_open()) {
// 文件已成功打开,可以进行读取操作
}
读取数据,使用>>提取运算符从文件中读取数据
std::ifstream file("example.txt");
if (file.is_open()) {
std::string str;
int num;
file >> str >> num;
std::cout << "Read string: " << str << ", number: " << num << std::endl;
}
检查文件状态,is_open是否成功打开,good()文件流是否处于有效状态,fail检查是否错误
std::ifstream file("example.txt");
if (!file) {
std::cerr << "Failed to open the file." << std::endl;
}
文件模式
std::ios::in
:默认模式,表示文件用于读取。std::ios::binary
:以二进制模式打开文件,而非文本模式
std::ifstream file("example.txt", std::ios::binary);