C/C++ 不定参函数

开发者福利!热门AI工具限时免费用 购周边即赠Coding Plan Lite,Claude Code、Cursor等20+工具畅享,效率翻倍! 阅读详情

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

 专门用于文件输出操作,就是将数据写入文件fstreamstd::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> 头文件,它用于文件输入操作,即从文件中读取数据ifstreamstd::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);

 

不定参数的应用 不定参数的应用不定参数当年做为C/C++语言一个特长被很多人推崇,但是实际上这种技术并没有应用很多。除了格式化输出之外,我实在没看到多少应用。主要原因是这种技术比较麻烦,副作用也比较多,而一般情况下重载函数也足以替换它。尽管如此,既然大家对它比较感兴趣,我就简单总结一下它的使用和需要注意的常见问题。原理刚学C语言的时候,一般人都会首先接触printf函数。通过这个函数,你可以打印不定 阅读详情

相关推荐

C语言不定参函数

在我们平常调用函数的时候,会进行传参,调用的函数也会有参数去接收,数量和类型都是对应的。而不定参函数是指对一个函数传参,参数的个数可以不确定。接下来,我就简单的叙述一下不定参函数的原理及应用。   在我们刚学C语言的时候,大多会首先接触printf();函数。通过这个函数,我们可以打印变量到屏幕,如: printf("%d", a); printf("%d,%d",b,c); pri...

dianqihan的博客 3699

C/C++不定参函数使用

上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N>=0)个模版参数。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。由于语法不支持使用args[i]这样方式获取可变参数,所以我们的用一些奇招来一一获取参数包的值。其中,strp是一个指向char指针的指针,用于存储格式化后的字符串的地址;例:写一个自己的打印函数

m0_70289867的博客 811

不定参函数、C/C++风格不定参函数

本文系统介绍了不定参函数的三种实现方式:C风格宏函数、C风格不定参函数C++可变参数模板。在C宏函数部分,重点讲解了__VA_ARGS__和##运算符的用法,以及__FILE__和__LINE__宏的应用;C风格函数部分详细解析了va_list、va_start等宏的使用方法和内存管理机制;C++模板部分则深入探讨了可变参数模板、递归展开、完美转发等现代C++特性。通过对比分析,文章指出C++模板方法在类型安全性和灵活性上的优势,同时保留传统方法在特定场景下的适用性,为开发者提供了全面的技术参考。

祯的博客 1221

C++ 函数中的不定参

.定义 所谓不定参数,指的就是在函数的定义中

Vergesson 1万+

C语言C++不定参数解析

函数传参传几个呢?两个太少,三个太多,我传一坤个,不定参数来帮你

wzh18907434168的博客 2876

C/C++不定参函数

在学习C语言的时候,往往使用printf进行打印,其中printf函数就是一个不定参函数,在这个函数内部可以根据格式化字符串中格式化的字符,分别获取不同的参数进行数据的格式化。这里对这类不定参的使用简要做出小结。

Kai的学习笔记 1374

C/C++ 不定参函数不定参函数实现方式

上述代码是基于c++11的,c++11对sizeof也增加了能对参数包进行取大小,sizeof使用的时候需要声明...是一个参数包,这个参数包的变量需要使用()进行括起来。通过不定参函数认识到了一个宏,__VA_ARGS__ 这个宏是在stdarg.h 头文件下的,查看下这个文件下有多少个“接口”。不定参变量就是在传入参数的时候,不确定需要传入的个数是多少,例如printf();需要注意的是,使用vasprintf时,需要使用一个定义宏,不然会报错。这是一个宏,使用的时候需要包含头文件。

小胡同学的博客 680

c/c++不定参函数的使用

【代码】C语言 不定参函数的使用。

weixiao0430的博客 149

C/C++不定参技术详解:从宏函数到可变参数模板的实战指南

在C/C++编程中,可变参数处理是提升代码灵活性和复用性的关键技术。其核心原理在于允许函数或宏接受数量不定参数,从而适应不同调用场景。从技术实现层面看,主要分为预处理阶段展开的不定参函数和运行时处理的C风格不定参函数不定参函数通过__VA_ARGS__等预处理器机制实现,虽然性能零开销,但存在类型安全缺失和调试困难等陷阱;而C风格不定参函数依赖va_list等标准库API,虽兼容性好却存在参数类型安全隐患。现代C++通过可变参数模板提供了类型安全的编译期解决方案,结合std::initializer

weixin_33738982的博客 358

C/C++不定参技术深度解析:从宏函数到可变参数模板的实战指南

在C/C++编程中,函数参数处理是基础且关键的技术环节。其核心原理在于函数调用时参数的传递与访问机制,传统固定参数函数在需要处理动态数量参数时显得力不从心。不定参技术的技术价值在于极大提升了代码的灵活性与可复用性,允许开发者创建能够适应不同调用场景的通用接口。这一特性在日志系统、调试工具、泛型容器和格式化输出等应用场景中尤为重要,例如实现类似printf的格式化输出或创建灵活的日志宏。本文聚焦于C/C++中两种核心的不定参实现方式:不定参函数不定参函数,通过对比分析它们在类型安全、性能开销和应用场景上的

weixin_30899023的博客 242

【C/C++】可变参数不定参函数的使用

如题

m0_67470729的博客 2209

C++项目实战——基于多设计模式下的同步&异步日志系统-②-前置知识补充-不定参函数

C++项目实战——基于多设计模式下的同步&异步日志系统-②-前置知识补充-不定参函数

花想云的博客 1151

【同步/异步 日志系统】 --- 前置技术

但是这种做法是存在线程安全的,有可能多个线程同时发现指针为null,同时去new多个对象,不满足单例的要求,此时我们需要进行加锁来保证多线程情况下只调用一次。

2301_79448270的博客 850

日志系统第二弹:设计模式介绍,C和C++不定参函数的介绍

设计模式介绍、C和C++不定参函数的介绍

Wzs040810的博客 1323

不定参函数

3 种不同的不定参函数学习

Elena's Blog 1650

C/CC 不定参函数

vasprintf 是 POSIX 标准的一部分,并不是 C 标准库的一部分,因此它在某些平台上可能不可用。它类似于 sprintf,但不同之处在于它使用了 va_list 来处理变长参数,并且它会自动为格式化后的字符串分配足够的内存。stdarg.h 是 C 语言标准库中的一个头文件,用于处理变长参数(variable arguments)。第二个参数函数的最后一个已知的命名参数。访问变长参数中的每一个参数,需要指定要访问的参数类型。返回的是参数包中包含的元素的数量,即参数个数。

JK01WYX的博客 994

C和C++中的 不定参函数

1 什么是不定参函数? 特征 :函数参数列表写的是 '…'即 用省略号忽略函数后边的参数,在C语言中我们也接触了一些 例如printf();函数, eg: int B(int a, double b,…) { return 0; } 注意 C 语言中的不定参函数C++中的不定参函数的区别 :像下面这种 : 在C语言中不合法,因为C语言要求 ,不定参函数至少应该有一个知道类型的参...

快乐星球 493

不定参函数的使用

C语言c++风格不定参函数的相关讲解介绍

2301_80343607的博客 1045

C/C++不定参

C++中,我们就可以使用template来定义一个可变模版参数,最终通过可变模版参数来解决问题,其实他也是运用的递归的原理,递归的去对每一个参数进行调用,我们在这儿定义一个空的xprintf的作用就在于最后递归调用过去为空时就会发生一些问题,所以我们在这儿就定义一个空的调用,调用到最后打印一个换行符就可以了。当然,上面这一段代码是针对于某个特定的类型来说的,如果我们需要打印的是字符串的话,就需要使用到我们的vasprintf函数。它的作用是初始化一个。类型的变量和可变参数的最后一个固定参数的地址。

2303_77100822的博客 1600
上一篇: 高性能日志系统
下一篇: 数据库 概念和索引总结
gma999
博客等级 码龄5年 2132粉丝 185原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值