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);
相关推荐
OpenAnolis小助手3 小时前
一句话看透 JVM,SysOM 诊断 Skill 新增 Java 应用诊断能力
java·开发语言·jvm·阿里云·操作系统控制台·sysom
小玮看世界3 小时前
[Python]OD算法在OD实际运用转化参考清单
开发语言·python·算法
ForteScarlet4 小时前
Kotlin 2.4.20 现已发布,新特性多不多?
android·java·开发语言·开源·kotlin·jetbrains
萧瑟余晖4 小时前
Java深入解析篇六十二之安全机制详解
java·开发语言
老赵的博客5 小时前
c++ QT之动态库加载问题
c++·qt
菜鸟~noob2335 小时前
【电子战】 第01篇:噪声、门限与虚警/检测概率(PFA/PD)【含matlab代码】
开发语言·matlab
奇牙coding1235 小时前
GPT-6-Astra API 接入教程:OpenRouter 路由配置 + Python/curl 示例 + 静默降级踩坑
开发语言·python·gpt·ai
(Charon)5 小时前
【C++】定时器进阶:使用最小堆管理定时任务
c++·算法
hansang_IR5 小时前
【题解】 [省选联考 2021 A/B 卷] 卡牌游戏
c++·算法
lzx_0026 小时前
C++11(一)
开发语言·c++·算法