va_list 使用总结

  • 前言
  • 可变参数函数是什么?
    • 示例
    • 分析
      • 参数 param_num 的作用
      • 获取第一个可变参数
      • 获取第二个可变参数
      • 获取第三个可变参数
      • 小总结
    • 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常用情况经验积累。

va_list 使用总结相关推荐

  1. va_list和va_start和((A*)0)-a

    C语言函数是从右到左入栈的 va_list ap;//=char *ap;(一个字符指针) va_start(ap,v) 中 ( ap = (va_list)_ADDRESSOF(v) + _INTS ...

  2. va_list、va_start和va_end使用

    我们知道va_start,va_arg,va_end是在stdarg.h中被定义成宏的,由于1.硬件平台的不同 2.编译器的不同,所以定义的宏也有所不同. 在ANSI C中,这些宏的定义位于stdar ...

  3. linux c va_list 32位和64位的差异

    在将程序从32位机器移植到64位机器的过程中经常出现一些奇奇怪怪的错误,这里记录一下在使用可变参数的过程中导致在32位机器上正常运行的程序移植到64位机器上之后出现段错误的发现过程以及解决方案. 首先 ...

  4. 理解可变参数va_list、va_start、va_arg、va_end原理及使用方法

     参考: http://www.360doc.com/content/12/0309/10/4025635_192940551.shtml http://www.cnblogs.com/Annie ...

  5. c语言解决函数变参数问题 va_list

    前言: 看到sprintf,swprintf之类的可变参数格式化函数,是否想过我们能写一个自定义的类似的函数吗?答案是很定的,下面来介绍一种方法,用va_list,va_start, va_end来实 ...

  6. 可变参数__VA_ARGS__使用和va_list使用小结

    1. #用来把参数转换成字符串 #define P(A) printf("%s:%d\n",#A,A); #define SQUARE(x) printf("The sq ...

  7. va_list 简介

    va_list 简介 (2011-04-16 10:31:17) 转载▼ 标签: 杂谈 分类:c/c 知识 va_list是一个宏,由va_start和va_end界定. typedefchar* v ...

  8. va_list/va_start/va_arg/va_end深入分析

    va_list/va_start/va_arg/va_end这几个宏,都是用于函数的可变参数的. 我们来看看在vs2008中,它们是怎么定义的: 1: ///stdarg.h 2: #define v ...

  9. 转载--va_list

    链接:va_list百科 1:概述 VA_LIST 是在c语言中解决变参问题的一组宏,所在头文件:#include <stdarg.h> 2:成员 #ifdef _M_ALPHA type ...

  10. va_start、va_end、va_list的使用

    http://www.cnblogs.com/rainduck/archive/2010/11/10/1873417.html 1:当无法列出传递函数的所有实参的类型和数目时,可用省略号指定参数表 v ...

最新文章

  1. 《数据挖掘导论》实验课——实验一、数据处理之Numpy
  2. eeglab教程系列(2)-加载、显示数据
  3. myeclipse6.0.1(内置了eclipse)安装及其配置
  4. jdbc与java数据库编程_JDBC与JAVA数据库编程
  5. DNSPod揭6省断网重大事故内幕:网游私服恶斗
  6. 拓端tecdat|数据视域下图书馆话题情感分析
  7. js 中 ! + - ~ 符号和匿名函数
  8. 功能强大且易用的云打印解决方案
  9. oracle中ipad是什么意思,oracle 函数 Ipad的简单用法
  10. 《凤凰项目-一个IT运维从传奇故事》总结
  11. [ExtJS] Tpl模板中的extjs控件无法失焦处理
  12. 哪个106短信服务平台好?这4点要参考!
  13. QQ\微信等聊天记录备份与恢复
  14. win10 oracle怎样卸载,怎么完全卸载Oracle?(Win10) 以Oracle 11g为例
  15. 第二类换元法之倒代换习题
  16. sharding异常之no table route info
  17. 龙之谷2微信哪个服务器,龙之谷2微信区
  18. JQuery 添加元素(jquery对象),删除元素( .remove())
  19. VScode修改行间距
  20. GPS网的基线计算以及平差处理(使用Python绘制误差椭圆,C++处理数据并输出Qxx矩阵)

热门文章

  1. 对自己的大学期望与目标
  2. 大学学习的期望与目标
  3. ebpf 研究之 XDP 技术
  4. 明年英特尔将推出最新Skylake Xeon芯片
  5. CDH和Hadoop的区别
  6. html5调用papy支付,Payment
  7. 域名备案和icp备案有哪些区别?
  8. 删除文件名含有特殊字符的文件
  9. 【Java小项目】打飞机
  10. Linux下ln命令建立软硬链接