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. monkey 自己总结

    adb shell monkey -p com.test.smstest --pct-touch 50 --pct-motion 30 --pct-nav 20 -s 10 -v --throttle ...

  2. (转)Sqoop中文手册

    Sqoop中文手册 1.     概述 本文档主要对SQOOP的使用进行了说明,参考内容主要来自于Cloudera SQOOP的官方文档.为了用中文更清楚明白地描述各参数的使用含义,本文档几乎所有参数 ...

  3. 数据库DDL审计

    一.为什么需要数据库DDL审计? DDL在生产系统中扮演非常重要的作用. 1)首先从业务角度来说,DDL可能意味着表结构变更,意味着新的版本即将发布,是个重要的时刻. 2)其次从运维角度来说,DDL尤 ...

  4. C# GDI+发生一般性错误(A generic error occurred in GDI+))

    解决思路: 1. 因为 .net GDI+ 是对底层 的封装. 所以可以尝试用 Marshal.GetLastWin32Error();函数获得底层错误代码. try{ image.Save(file ...

  5. Android布局整理Relative/Linear

    1.RelativeLayout布局 android:layout_centerHorizontal 水平居中 android:layout_centerVertical 垂直居中 android:l ...

  6. mysql--sqlalchemy.exc.IntegrityError: (IntegrityError) (1215, 'Cannot add foreign key constraint'

    今天在使用mysql时遇到的问题,最后发现问题是,数据类型与外键数据类型不同,改正过来就没有问题了.

  7. 每天一个percona 工具 --- pt-kill

    主要用途:pt-kill是用来kill MySQL连接的一个工具,在MySQL中因为空闲连接较多导致超过最大连接数,或某个有问题的sql导致mysql负载很高时,需要将其KILL掉来保证服务器正常运行 ...

  8. Java—Servlet技术

    1  Servlet 概述 Servlet简介——开发动态web资源的技术Sun公司在API提供了一个servlet接口,如开发一个java程序向浏览器输出数据:1)编写一个java类,实现servl ...

  9. WPF知识总结(一)

    在一个项目中, 发现有的项目引用的动态库是一个网页地址,下面就看看这个网页地址怎么来的. 目标:新建一个WPF工程,实现一个用户控件的功能.在 工程中加入一个类库,然后在类库中增加一个用户控件页面,在 ...

  10. 安装Windows 10后PDF补丁丁等程序界面变得模糊的解决办法

    对于使用高分辨率屏幕且屏幕缩放比例在 100%以上的用户,升级到 Windows 10 后将发现许多程序的界面,例如QQ.电脑管家.Windows本身的服务管理程序等等,都变得非常模糊,<PDF ...