C/C++ 不定参函数

C语言不定参函数

函数用法总结

Va_list

  • **作用:**类型定义,生命一个变量,该变量被用来访问传递给不定参函数的可变参数列表
  • **用法:**供后续函数进调用,通过该变量访问参数列表
cpp 复制代码
typedefchar* va_list;

va_start

  • **作用:**初始化va_list变量,指向可变参数列表中的第一个参数,这个宏需要知道最后一个固定参数的位置,方便从其后开始读取可变参数
  • **参数:**ap(初始化的va_list变量)、last_artg(确定从哪里开始读取可变参数4)
cpp 复制代码
voidva_start(va_list ap, last_arg);

va_arg

  • **作用:**va_arg宏用于获取可变参数列表中的下一个参数,并将其转换为指定的类型
  • **参数:**ap,type指定要获取的参数类型
  • **返回值:**返回指定类型的下一个参数值,并将va_list 变量指向下一个参数
cpp 复制代码
type va_arg(va_list ap, type);

va_end

  • **作用:**用于清理va_list变量
  • **参数:**ap需要清理的va_list变量
cpp 复制代码
voidva_end(va_list ap);

使用事例

使用事例

  • 至少定义一个固定参数的函数,然后后面的参数使用... 表示
  • va_list args:定义一个变量,用于存储不定参数的列表
  • va_start(args, fixeArg):初始化args,并让其指向可变参数列表的开始;fixeArg则是最后一个固定参数,用于确定从哪里开始寻找可变参数
  • va_arg(args,int):获取当前参数,类型设置为int,并将指针移动到下一个参数
  • va_end(args):清理va_list,结束不定参数的获取
cpp 复制代码
#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;
}
cpp 复制代码
#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
cpp 复制代码
intvasprintf(char **strp, constchar *fmt, va_list ap);
  • **函数create_message:**初始化变量ap,从fmt之后的参数开始获取可变参数
  • **vasprintf:**根据fmt中的格式化字符串,以及ap生成最终字符串,动态分配内存存储生成的字符串,并将其存储在strp中(将格式化字符格式和可变参数结合,生成固定格式的消息,存储在strp中)
cpp 复制代码
#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:**宏的可变参数部分为空的时候,可以自动取出前面的逗号,避免语法错误
cpp 复制代码
#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语言风格相同

cpp 复制代码
#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可变模版

通过递归模版展开

cpp 复制代码
#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折叠表达式

cpp 复制代码
#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

专门用于文件输出操作,就是将数据写入文件fstreamstd::basic_ofstream 的一个实例化,std::basic_ofstream 是一个模板类,用于处理各种字符类型的文件输出。ofstream 专门处理 char 类型的文件

基本使用总结

文件打开关闭

  • ofstream对象可以利用自身构造或者open方法打开文件,文件只要被打开,程序就可以将数据写入文件
  • 程序结束或者该对象销毁的到时候,文件会自动关闭,也可以手动关闭文件,即调用close()方法
cpp 复制代码
std::ofstream file("example.txt");
if (file.is_open()) {
    // 文件已成功打开,可以进行写入操作
}

写入数据

  • 支持向文件写入多种类型的数据
cpp 复制代码
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:检查是否出现了某种错误
cpp 复制代码
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:以二进制模式打开文件,而非文本模式
cpp 复制代码
std::ofstream file("example.txt", std::ios::app);

自动资源管理

  • 内部是RAII模式管理文件资源,只要该对象超出作用域,文件就会自动关闭
cpp 复制代码
{
    std::ofstream file("example.txt");
    file << "This will be written to the file." << std::endl;
} // file 对象超出作用域,文件自动关闭

ifstream

标准库中的一个类,属于 <fstream> 头文件,它用于文件输入操作,即从文件中读取数据ifstreamstd::basic_ifstream 的一个实例化,std::basic_ifstream 是一个模板类,用于处理各种字符类型的文件输入。ifstream 专门处理 char 类型的文件

基本使用总结

文件打开和关闭,与Ofstream相同,构造函数打开,打开后程序可以从文件中读取数据,程序结束或者对象被销毁的时候,文件自动关闭。手动关闭就是close()

cpp 复制代码
std::ifstream file("example.txt");
if (file.is_open()) {
    // 文件已成功打开,可以进行读取操作
}

读取数据 ,使用**>>提取运算符**从文件中读取数据

cpp 复制代码
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检查是否错误

cpp 复制代码
std::ifstream file("example.txt");
if (!file) {
    std::cerr << "Failed to open the file." << std::endl;
}

文件模式

  • std::ios::in:默认模式,表示文件用于读取。
  • std::ios::binary:以二进制模式打开文件,而非文本模式
cpp 复制代码
std::ifstream file("example.txt", std::ios::binary);
相关推荐
Theodore_102227 分钟前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
网易独家音乐人Mike Zhou1 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
‘’林花谢了春红‘’2 小时前
C++ list (链表)容器
c++·链表·list
----云烟----2 小时前
QT中QString类的各种使用
开发语言·qt
lsx2024062 小时前
SQL SELECT 语句:基础与进阶应用
开发语言
开心工作室_kaic3 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
向宇it3 小时前
【unity小技巧】unity 什么是反射?反射的作用?反射的使用场景?反射的缺点?常用的反射操作?反射常见示例
开发语言·游戏·unity·c#·游戏引擎
武子康3 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
转世成为计算机大神4 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
搬砖的小码农_Sky4 小时前
C语言:数组
c语言·数据结构