`
简单的茶
  • 浏览: 87582 次
  • 性别: Icon_minigender_2
  • 来自: 昆明
社区版块
存档分类
最新评论

va_start和va_end使用详解

 
阅读更多
src :   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的介绍。
分享到:
评论

相关推荐

    stdarg.h中三个宏va_start ,va_arg和va_end的应用

    ### stdarg.h中三个宏va_start,va_arg和va_end的应用详解 在C语言中,经常需要编写能够接受可变数量参数的函数。一个典型的例子就是`printf`函数,它可以接受任意数量和类型的参数。为了支持这种功能,C语言提供了...

    va_start、va_end、va_list的详细使用方法

    ### va_start、va_end、va_list的详细使用方法 #### 概述 在C语言中,函数参数的数量通常是固定的。然而,在某些情况下,我们可能希望一个函数能够接收可变数量的参数。例如,`printf`函数就是一个很好的例子,它...

    详解_C语言可变参数_va_list和_vsnprintf及printf实现

    这种灵活性主要由两个关键组件实现:`va_list`类型和宏`va_start`, `va_arg`和`va_end`。下面我们将逐一解析这些概念。 **`va_list`** `va_list`是一个特殊类型,用于存储可变参数列表的状态。当函数被调用时,`va...

    printf函数实现详解

    这个示例中,我们可以看到 printf 函数使用 va_list 来存储参数,并使用 va_start 和 va_arg 宏来访问参数。 四、printf 函数的实现流程 printf 函数的实现流程可以分为以下几个步骤: 1. va_start(ap, format); ...

    C语言中可变参数实现分析

    通过使用`va_list`、`va_start`、`va_arg`和`va_end`宏,程序员可以构建能够适应各种输入场景的函数。然而,值得注意的是,由于这些宏直接操作栈上的数据,不当使用可能会导致栈溢出或其他运行时错误。因此,在设计...

    C语言参数个数可变函数详解

    在C语言中实现可变参数函数的关键在于`stdarg.h`头文件中的几个宏:`va_list`、`va_start`、`va_arg`和`va_end`。首先,`va_list`定义了一个类型,用于存储可变参数列表的指针。接着,`va_start`宏初始化`va_list`,...

    CC++变长参数用法

    通过以上示例,我们可以看到,在C/C++中处理变长参数时,通常会涉及到`va_list`、`va_start`、`va_arg`和`va_end`这几个关键概念。在实际应用中,还需要注意以下几点: 1. **类型安全**:变长参数列表不进行类型...

    des算法代码实现输出matlab-CodePracticeAndLibBuild:算法训练日志

    va_start和va_end使用详解 Keil中使用宏编译来定义DEBUG输出 typedef void (*func)(void *) 的含义 快速运算之:使用与运算符代替求余运算符的技巧 多文件目录下makefile文件递归执行编译所有c文件 STM32F1做RSA,...

    vsprintf()

    - **资源管理**:正确使用 `va_start()` 和 `va_end()` 宏,确保资源得到妥善管理。 #### 六、总结 `vsprintf()` 函数提供了强大的字符串格式化功能,特别适用于需要动态参数的情况。通过合理使用 `va_list` 和相关...

    c可变参数用法 命令

    可变参数函数的核心在于`va_list`、`va_start`、`va_arg`和`va_end`这几个宏的使用。下面分别解释这些宏的作用: 1. **va_list**: 用于声明一个可变参数列表。 2. **va_start**: 初始化可变参数列表。 3. **va_arg*...

    SimpleObserver.rar

    C++标准库提供了`stdarg.h`头文件,其中包含的`va_list`、`va_start`、`va_arg`和`va_end`宏可用于处理可变参数列表。通过这些宏,我们可以创建一个函数或方法,该函数能够接受任意数量和类型的参数。这使得我们的...

    C语言函数库详解.docx

    这些宏包括va_list、va_start、va_arg和va_end等。 stddef.h:公共定义 stddef.h头文件提供了一组公共定义,用于定义一些基本的数据类型和宏。这些定义包括NULL、offsetof和ptrdiff_t等。 stdio.h:输入输出 ...

    C语言可变参数函数详解示例

    2. `va_start`: 这个宏用于初始化`va_list`变量`ap`,以便后续使用`va_arg`和`va_end`。它的第二个参数`last`是可变参数列表之前最后一个已知类型参数的名字。 3. `va_arg`: 这个宏用于从参数列表中获取下一个参数...

    改变字体颜色-c语言UNIX

    描述“改变字体颜色 c语言 UNIX linux”进一步强调了本文将关注于使用C语言,在UNIX和Linux系统中实现字体颜色的变化。 #### 知识点详述 1. **ANSI转义序列基础** 在UNIX和Linux系统中,可以通过ANSI(American ...

    C++函数详解

    7. **变长参数(Variable Length Argument List)**:使用`...`来表示函数可以接受任意数量的参数,通常与`va_list`, `va_start`, `va_arg`和`va_end`宏一起使用,但这种做法不常见,且不利于编译器进行类型检查。...

    printf函数族用法大全

    ### printf函数族用法详解 #### 一、概述 `printf`函数族是C语言中极为重要的函数之一,主要用于格式化输出数据。该家族包括多种不同的函数,它们各自有不同的应用场景和特点。本篇文章将详细介绍`printf`函数族的...

    C语言之精华总结,不妨看看。。

    例如,`void print(int count, ...)`可以接收任意数量的参数,通过`va_list`, `va_start`, `va_arg`和`va_end`进行处理。 学习C语言需要理解这些核心概念,并通过实践加深理解。对于初学者,指针可能是一项挑战,但...

    系统调用分析

    #### 二、open系统函数实现详解 本节将深入探讨Linux内核中`open`系统调用的具体实现细节,包括其源码解析以及宏定义展开过程。 ##### 1.1 函数实现 `open`系统调用的主要实现在`fs/open.c`文件中,具体实现如下...

    C语言函数库详解.doc

    `va_list`, `va_start()`, `va_arg()`, `va_end()`等宏定义了处理可变参数的方法。 10. `&lt;stddef.h&gt;`:公共定义 定义了一些标准的类型,如`size_t`用于表示内存大小,`ptrdiff_t`用于表示两个指针间的差异,以及`...

Global site tag (gtag.js) - Google Analytics