va_start和va_end使用详解

原文地址:http://www.cnblogs.com/hanyonglu/archive/2011/05/07/2039916.html

本文主要介绍va_start和va_end的使用及原理。

  在以前的一篇帖子Format MessageBox 详解中曾使用到va_start和va_end这两个宏,但对它们也只是泛泛的了解。

  介绍这两个宏之前先看一下C中传递函数的参数时的用法和原理:

1.在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表

void foo(...); void foo(parm_list,...);
这种方式和我们以前认识的不大一样,但我们要记住这是C中一种传参的形式,在后面我们就会用到它。

2.函数参数的传递原理

  函数参数是以数据结构:栈的形式存取,从右至左入栈。

  首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下: void func(int x, float y, char z);   那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。
  下面是 <stdarg.h> 里面重要的几个宏定义如下: 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 ); va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。 <Step 1> 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap); <Step 2> 然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数; <Step 3> 然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置; <Step 4> 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。
例如 int max(int n, ...); 其函数内部应该如此实现:
#include <iostream.h>
void fun(int a, ...)
{   
  int *temp = &a;

  temp++;

  for (int i = 0; i < a; ++i)   

  {     

    cout << *temp << endl;     

    temp++;   

  }

}

int main()

{

  int a = 1;

  int b = 2;

  int c = 3;

  int d = 4;

  fun(4, a, b, c, d);

  system("pause");

  return 0;

}

Output:: 1 2 3 4

3:获取省略号指定的参数   在函数体中声明一个va_list,然后用va_start函数来获取参数列表中的参数,使用完毕后调用va_end()结束。像这段代码: void TestFun(char* pszDest, int DestLen, const char* pszFormat, ...) { va_list args; va_start(args, pszFormat); //一定要“...”之前的那个参数 _vsnprintf(pszDest, DestLen, pszFormat, args); va_end(args); }
4.演示如何使用参数个数可变的函数,采用ANSI标准形式
#include 〈stdio.h〉
#include 〈string.h〉
#include 〈stdarg.h〉
/*函数原型声明,至少需要一个确定的参数,注意括号内的省略号*/
int demo( char, ... );
void main( void )
{
   demo("DEMO", "This", "is", "a", "demo!", "");
}
/*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;
}

以上是对va_start和va_end的介绍。

[转载]va_start和va_end使用详解的更多相关文章

  1. [转载]C/C++可变参数之va_start和va_end使用详解

    本文主要介绍va_start和va_end的使用及原理. 在以前的一篇帖子Format MessageBox 详解中曾使用到va_start和va_end这两个宏,但对它们也只是泛泛的了解. 介绍这两 ...

  2. va_start和va_end使用详解(转载)

    转自:http://www.cnblogs.com/hanyonglu/archive/2011/05/07/2039916.html 本文主要介绍va_start和va_end的使用及原理. 在以前 ...

  3. [转]va_start和va_end使用详解

    转载地址:http://www.cnblogs.com/hanyonglu/archive/2011/05/07/2039916.html  本文主要介绍va_start和va_end的使用及原理. ...

  4. va_start和va_end使用详解

    本文主要介绍va_start和va_end的使用及原理. 介绍这两个宏之前先看一下C中传递函数的参数时的用法和原理: 1.在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表 ...

  5. [转载]java中import作用详解

    [转载]java中import作用详解 来源: https://blog.csdn.net/qq_25665807/article/details/74747868 这篇博客讲的真的很清楚,这个作者很 ...

  6. [转载]AxureRP 7.0部件详解(一)

    本文为Axure RT7.0教程,本章主要介绍menu菜单.table表格.Tree Widget 树部件三个部件,后续将持续更新...... Menu 菜单 常用案例 网站导航菜单部件通常用于母板之 ...

  7. 【转载】Hadoop历史服务器详解

    免责声明:     本文转自网络文章,转载此文章仅为个人收藏,分享知识,如有侵权,请联系博主进行删除.     原文作者:过往记忆(http://www.iteblog.com/)     原文地址: ...

  8. 转载:DNS解析过程详解

    2015-09-20 此好文是转载,如有侵权联系我,立马删掉 DNS的几个基本概念: 一. 根域 就是所谓的“.”,其实我们的网址www.baidu.com在配置当中应该是www.baidu.com. ...

  9. 转载:MySQL EXPLAIN 命令详解学习

    转载自:https://blog.csdn.net/mchdba/article/details/9190771 MySQL EXPLAIN 命令详解 MySQL的EXPLAIN命令用于SQL语句的查 ...

随机推荐

  1. 本周psp(观众页面)

    个人项目PSP(观众页面) 计划 30min 估计要用多长时间 3d 开发 5h 需求分析 1h 生成设计文档 1h 设计复审 50min 代码规范 20min 具体设计 40min 具体编码 3h ...

  2. javascript学习内容--object.style.display="value" value值为“”none“隐藏”或 "block"显示

    <head> var mychar=document.getElementById("con"); function hidden(){ mychar.style.di ...

  3. Python_Day7_面向对象学习

    1.面向对象编程介绍 2.为什么要用面向对象进行开发? 3.面向对象的特性:封装.继承.多态 4.类.方法. 面向过程 VS 面向对象 编程范式 编程是程序员用特定的语法+数据结构+算法组成的代码来告 ...

  4. STM32 串口固件库中定义的几个中断标志位什么意思?

    在stm32f10x_usart.h中以上几个宏,很没有规律,诈一看还真不知道为什么会这么定义,其实通过代码就很容易明白: D7~D5:代表中断标志位对应的中断使能位在 CR1.CR2还是CR3寄存器 ...

  5. linux笔记:shell编程-文本处理命令

    cut(字段提取命令,也叫列提取命令): printf(格式化输出命令): awk(awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理): sed(sed是一个很好 ...

  6. forever让nodejs应用后台执行

    nodejs一般是当成一条用户命令执行的,当用户断开客户连接,运用也就停了,很烦人.如何让nodejs应用当成服务,在后台执行呢? 最简单的办法: $ nohup node app.js & ...

  7. 对Android开发者有益的40条优化建议

    下面是开始Android编程的好方法: 找一些与你想做事情类似的代码 调整它,尝试让它做你像做的事情 经历问题 使用StackOverflow解决问题 对每个你像添加的特征重复上述过程.这种方法能够激 ...

  8. [sqoop1.99.7] sqoop实例——数据ETL

    一.创建一个mysql的link MySQL链接使用的是JDBC,必须有对应的驱动文件jar,还得有对应的访问权限,请确保能在server端访问MySQL.确保mysql的jar包已经导入到${SQO ...

  9. PL/SQL导出到execl中,数据前面的0发生丢失的解决办法

    ERR出现的场景再现: 使用 PL/SQL导出按钮,选择‘CSV文件’,保存为1.csv,后用execl打开,复制到VuGen中作为login脚本的参数化文件username. ERR及发现过程: 在 ...

  10. ALT+TAB切换时小图标的添加 界面透明 屏幕大小 竖行字体 进程信息

    一,ALT+TAB切换时小图标的添加 Dlg类中添加变量 protected: HICON m_hIcon; #define IDR_MAINFRAME 128 ICON IDR_MAINFRAME, ...