【C/C++】可变参数列表

一、可变参数宏(VA_ARGS__)

#include <stdio.h>

//...表示不定参数列表,__VA_ARGS__是宏,代表不定参数列表部分
// __FILE__    宏  代表 : 文件名
//__LINE__     宏  代表 : 行号
//__VA_ARGS__ 用于在宏替换部分中,表示可变参数列表;
//当可变参数的个数为0时,##起到把前面多余的","去掉的作用,否则会编译出错
//要注意空格的使用
#define LOG(format, ...) printf("[%s:%d]" format,__FILE__,__LINE__,##__VA_ARGS__);

int main()
{
    LOG("可变参数列表\n");
    //最终展开的代码相当于printf("[%s:%d]" "可变参数列表\n",__FILE__,__LINE__)
    //如果不加##,会报错
    printf("%d ""nihao\n",6);//可以编译通过
    return 0;
	//上面的 [%s:%d] format 会自动拼接在一起 都是字符串
}

二、C风格不定参使用

#include <stdio.h>
#include <stdarg.h>

void print(int cnt, ...)
{
    // 使用之前定义va_list 变量
    va_list ap;
    // void va_start(va_list ap,param);
    va_start(ap, cnt); // 获取cnt参数之后的第一个参数的地址即第一个不定参地址
    for (int i = 0; i < cnt; ++i)
    {
        // type va_arg(va_list ap,type);
        int num = va_arg(ap, int); // 获取不定参
        printf("param[%d]:%d\n", i, num);
    }
    // void va_end(va_list ap);
    va_end(ap); // 使用完将ap指针置空
}
//在print函数的实现中,我们首先通过va_start初始化ap变量,然后使用一个循环来遍历cnt个可变参数。
//在循环体中,我们使用va_arg逐个获取可变参数,并使用printf函数打印每个参数的值。最后,我们使用va_end清理ap变量。

int main()
{
    print(3,1,2,3);
    return 0;
}
//运行结果
//param[0]:1
//param[1]:2
//param[2]:3

va_list ap

va_list ap:va_list是一个类型,用于存储可变参数的列表。在函数内部,我们定义了一个ap变量,它将用于访问可变参数列表中的实际参数。

va_start()函数 语法

void va_start(va_list ap,param);
//va_start(ap, cnt):va_start宏用于初始化ap变量,用于访问可变参数。
//它接受两个参数,第一个参数是一个va_list变量,
//第二个参数是可变参数列表的前一个参数(在本例中是cnt)。
//这个宏的作用是设置ap为指向第一个不定参数的指针。

va_arg()函数用于调用可变参数列表

type va_arg(va_list ap,type);
//va_arg(ap, int):va_arg宏用于访问可变参数列表中的实际参数。
//它接受两个参数,第一个参数是一个va_list变量,
//第二个参数是希望返回的参数类型。在本例中,我们使用int作为参数类型。
//这个宏的作用是返回当前可变参数列表中的下一个实际参数,
//并将指针ap移到下一个参数位置。

va_end()函数用于停止使用可变参数。

void va_end(va_list ap);
//va_end(ap):va_end宏用于结束对可变参数的访问。
//它接受一个参数,即一个va_list变量。
//这个宏的作用是清理va_list变量在访问参数列表时所做的任何操作,
//并将ap指针置空。

函数调用时参数的压栈顺序及内存使用

printf(const char* format,…)为例

  1. printf函数是一个不定参函数。
  2. 编译器通过format的%占位符的个数来获取参数的个数。
  3. 假设函数压栈顺序是从左至右,format先入栈,各个参数再入栈,最后pc入栈入栈完之后,想知道参数的个数就要读取format,但要读取format就得知道参数的个数,陷入了一个死循环。
  4. 如果函数压栈顺序是从右至左,未知个数的参数先入栈,format再入栈,最后压pc入栈。这时候要想知道参数的个数只需要将栈顶指针加2即可读取到format。
  5. 函数调用栈从高往低使用

使用不定参模拟实现printf

int vasprintf(char **strp, const char *fmt, va_list ap);

vasprintf 是一个 C 库函数,它可以通过可变参数创建一个格式化的字符串,并将其存储在动态分配的内存中。它的使用方法与 printf 类似,但它不会将结果打印到标准输出流中,而是将其存储在一个指向字符数组的指针中 成功返回字符串长度,失败返回-1

void MyPrint(const char *fmt, ...)
{
    va_list ap;
    va_start(ap,fmt);
    char* res;
    int ret=vasprintf(&res,fmt,ap);
    if(ret  !=-1)
    {
        printf(res);
        free(res);
    }
    va_end(ap);
    
}

三、C++风格不定参数的使用

需要借助C++11内的万能引用与完美转发及可变参数模版包

#include <iostream>
#include <cstdarg>
#include <memory>
#include <functional>

//特化
void CgagaPrint()
{
    std::cout<<std::endl;
}

template<class T,class ...Args>
void CgagaPrint(const T& v,Args&& ...args)
{
    std::cout<<v;
    if((sizeof ...(args))>0)
    {
    	//完美转发
        CgagaPrint(std::forward<Args>(args)...);
    }
    else
    {
        CgagaPrint();
    }
}

int main()
{
    CgagaPrint("nihao",6,5,4);
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

翻转的乌龟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值