va_start和va_end详解
1. 在C中,当无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表。例如:
void foo(...);
void foo(parm_list,...);
2. 函数参数的传递原理
函数参数是以栈的形式存取,从右至左入栈。
参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下:
void func(int x, float y, char z);
那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z。从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。举个例子如下:
#include <stdio.h> //获取参数列表中的所有参数,并打印
void PrintInt(int cnt, ...)
{ int *temp = &cnt;temp++;for (int i = 0; i < cnt; ++i) { printf("%d\n", *temp); temp++; }
}int main(void)
{int a = 1; int b = 2; int c = 3; int d = 4;PrintInt(4, a, b, c, d); return 0;
}
执行程序后输出:
1
2
3
4
3. 利用Va_start相关宏获取省略号指定的参数
typedef char* va_list;
void va_start ( va_list ap, prev_param ); /* ANSI version */
type va_arg ( va_list ap, type );
void va_end ( va_list ap );
说明:
1)va_list:一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。
2)va_start:对ap进行初始化,让ap指向可变参数表里面的第一个参数。第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;
3)va_arg: 获取参数。它的第一个参数是ap,第二个参数是要获取的参数的指定类型。按照指定类型获取当前参数,返回这个指定类型的值,然后把 ap 的位置指向变参表中下一个变量的位置;
4)va_end:释放指针,将输入的参数 ap 置为 NULL。通常va_start和va_end是成对出现。
使用上面的宏获取参数的步骤如下:
<Step 1> 定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);
<Step 2> 调用va_start ,对ap 进行初始化,让它指向可变参数表里面的第一个参数。
#include 〈stdio.h〉
#include 〈string.h〉
#include 〈stdarg.h〉
/*ANSI标准形式的声明方式,括号内的省略号表示可选参数*/
int demo( char msg, ... )
{ /*定义保存函数参数的结构*/va_list argp; int argno = 0; char *para; /*argp指向传入的第一个可选参数,msg是最后一个确定的参数*/ va_start( argp, msg ); while (1) { para = va_arg( argp, char*); if ( strcmp( para, "") == 0 ) break; printf("Parameter #%d is: %s\n", argno, para); argno++;
}
va_end( argp );
/*将argp置为NULL*/
return 0;
}
void main( void )
{ demo("DEMO", "This", "is", "a", "demo!", "");
}
va_start和va_end详解相关推荐
- [C语言] va_start和va_end详解
在C中,当无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表.例如: void foo(...); void foo(parm_list,...); 函数参数的传递原理 函数参数是以栈的 ...
- va_start和va_end使用详解
va_start和va_end使用详解 本文主要介绍va_start和va_end的使用及原理. 在以前的一篇帖子Format MessageBox 详解中曾使用到va_start和va_end这两个 ...
- 不确定函数参数个数时的处理,va_start和va_end使用详解
引出 在C中,当我们无法确定函数的所有实参的类型和数目时,可以用省略号指定参数表来处理,如: void foo(...); void foo(parm_list,...); 具体怎么使用呢,我 ...
- java va start_va_start和va_end使用详解
本文主要介绍va_start和va_end的使用及原理. 在以前的一篇帖子Format MessageBox 详解中曾使用到va_start和va_end这两个宏,但对它们也只是泛泛的了解. 介绍这两 ...
- Win32开发之Format MessageBox 详解
本文介绍在Windows程序开发中的MessageBox详解. 我们在在Windows程序设计中经常会涉及到一个格式化消息框,其代码如下: #include <windows.h> # ...
- linux 进程间通信 dbus-glib【实例】详解四(上) C库 dbus-glib 使用(附代码)(编写接口描述文件.xml,dbus-binding-tool工具生成绑定文件)(列集散集函数)
linux 进程间通信 dbus-glib[实例]详解一(附代码)(d-feet工具使用) linux 进程间通信 dbus-glib[实例]详解二(上) 消息和消息总线(附代码) linux 进程间 ...
- C 和 C++ 宏 详解
From:https://www.cnblogs.com/njczy2010/p/5773061.html C中的预编译宏详解:http://www.cppblog.com/bellgrade/arc ...
- 【C++】Web服务器项目所用到的函数详解
文章目录 1 Web服务器端监听 1.1 socket()函数 1.2 struct sockaddr和struct sockaddr_in结构体(INADDR_ANY) 1.3 bzero()函数 ...
- imx6ul 驱动详解
链表的知识: struct list_head {struct list_head *next, *prev; }; API函数 函数 功能 LIST_HEAD 声明并初始化双向链表. INIT_LI ...
最新文章
- Swift3.0语言教程删除字符与处理字符编码
- keepalived实现Tomcat服务双机热备
- MS UC 2013-0-虚拟机-标准化-部署-1-虚拟化-部署
- 1.2)深度学习笔记------神经网络的编程基础
- linux系统如何拨号上网连接,教你在Linux下如何使用ADSL拨号上网
- 用Python增强Excel的3大集成方法及用途
- 深入理解 OUI(Oracle Universal Installer)
- CodeIgniter的缓存小记
- mysql中数据导出到json文件的读取办法:
- css在线代码生成工具汇总
- gaussdb 日常运维命令总结【01】
- SVN Working copying #39;xxxxx/xxxx/xxxx#39; locked
- ubuntu中如何修改计算机名字
- 避雷器的测试项目及方法
- 微信公众平台找自己APPID
- 如何使用jquery修改css中带有!important的样式属性
- 财富:保罗·艾伦转向新领域
- Oracle EBS 开发如何获得并发请求REQUEST ID
- matlab计算涡度的函数_涡度的计算
- mysql检测不到运行库_请自行检查是否安装VC9运行库??