va_list 使用总结

前言

可变参数函数中,经常可以看到va_list、va_start、va_arg、va_end的使用。

可变参数函数是什么?

C语言中,printf函数,就是一个可变参数函数, 传入的参数数量是不确定的,可以传入多个。

printf函数原型:

int printf(const char *format, ...)

其中 ...,代表的就是可变参数,类似于省略号。

示例

下面通过一个简单的例子来体验可变参数函数。

static void va_list_test(int param_num, ...)
{
    int i = 0;
    va_list ap;
    va_start(ap, param_num);

    // 获取整型参数
    int arg1 = va_arg(ap, int);
    printf("arg1 = 0x%x\n", arg1);

    // 获取字符串
    char *arg2 = va_arg(ap, char *);
    printf("arg2 = %s\n", arg2);

    // 获取整型参数
    int arg3 = va_arg(ap, int);
    printf("arg3 = 0x%x\n", arg3);

    va_end(ap); // 注意需要 va_end(ap)
}

int main()
{
    va_list_test(2, 0x55, "arg test", 0x66);
    return 0;
}

输出结果如下:

arg1 = 0x55
arg2 = arg test
arg3 = 0x66

分析

上述例子,分别传入了4个参数, 2, 0x55, "arg test", 0x66, 可以从 va_list_test()函数传入参数的格式来看:

参数 param_num 的作用

为何需要param_num的固定参数?

  • 参数param_num, 即上述例子传入的数字2, 是固定参数。在上述例子无实际使用,用于给 va_start定位可变参数的位置va_start(ap, param_num)可以理解为,通过固定参数 param_num找到可变参数存储的起始位置, 并保存到了 ap变量中。这个与函数调用过程,函数帧入栈有关系,不展开讨论。

分别获取参数的过程

获取第一个可变参数

  • 参数0x55, 是整型参数, 由于 va_start(ap, param_num)通过传入的参数param_num定位到了可变参数的起始位置, 所以第一条获取整型参数 0x55, 用va_arg(ap, int)来获取第一个可变参数,0x55, 根据ap的位置,读取一个整型数值。

获取第二个可变参数

  • 参数"arg test", 是字符串参数, 由于读取了0x55后, ap的位置已经改变,变成了第二个参数的起始位置, 所以 va_arg(ap, char *)会以当前位置,获取一个字符串数值,所以得到"arg test"

获取第三个可变参数

  • 最后一个参数 0x66, 也是同理, 继续va_arg(ap, int)获取一个int 参数。

小总结

  1. 需要理解第一个固定参数的作用, 即va_start(ap, param_num), 同时需要关注 ap这个参数在获取可变参数中的意义, 可以理解为指针地址便宜,始终指向未获取的第一个可变参数地址
  2. 实验过程,尝试将可变参数1、2、3的获取顺序改变, 即获取顺序由 int 、 char *、int修改为 int 、int、char *, 会出现,参数2 获取异常, 参数3 printf后出现段错误。 所以变量的获取顺序不能出错,否则会有异常。
  3. va_start()va_end()需要配对出现。 不使用 va_end(ap)的后果,猜测可能是会导致内存泄漏。 可以自行查询。

va_list的常用方式

在大部分的实际情况下,va_list都是与printf()函数的使用类似。

如以下例子

int va_list_test(char *fmt, ...)
{
    char out[1024] = {0};

    va_list ap;
    va_start(ap, fmt);
    vsnprintf(out, sizeof(out), fmt, ap);
    printf("%s", out);
    va_end(ap);
}

int main()
{
    va_list_test("output : 0x%x, %s, 0x%x\n", 0x55, "arg test", 0x66);

    return 0;
}

输出结果如下:

output : 0x55, arg test, 0x66

分析

在上述例子中, 固定参数fmt实际就是传入的 "output : 0x%x, %s, 0x%x\n", 三个可变参数,分别按照 %x、%s、%x的形式,通过vsnprintf()格式化输入到out中。类似sprintf()

需要注意的是为什么用的vsnprintf()

先看下vsnprintf函数的原型, 可以看到最后传入的一个参数,就是 va_list 变量,所以使用 vsnprintf在上述例子中非常便捷。

int vsnprintf(char *str, size_t size, const char *format, va_list ap);

小总结

  1. 注意vsnprintf()函数的使用方法。
  2. va_list常用情况经验积累。
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值