`
美丽的小岛
  • 浏览: 309272 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

可变参数va_list

    博客分类:
  • c
  • c++
 
阅读更多

1.要在函数中使用参数,首先要包含头文件<stdarg.h>。这个头文件声明了一个va_list类型,定义了四个宏,用来遍历可变参数列表。

void va_start(va_list ap, last);

type va_arg(va_list ap, type);

void va_end(va_list ap);

void va_copy(va_list dest, va_list src);

下面详细介绍这些宏定义:

2.void va_start(va_list ap, last)

va_start必须第一个调用,它初始化va_list类型的变量ap,使ap指向第一个可选参数。参数 last 是可变参数列表(即函数原型中的省略号…)的前一个参数的名字,也就是最后类型已确定的函数参数名。因为这个参数的地址将会被宏va_start用到,所以最好不要是寄存器变量,函数,或者数组。

对于有可变长参数,但是在可变长参数前没有任何的固定参数的函数,如int func (...)是不允许的。  这是ANSI C所要求的,变参函数在...之前至少得有一个固定参数。这个参数将被传递给va_start(),然后用va_arg()和va_end()来确定所有实际调用时可变长参数的类型和值。

type va_arg(va_list ap, type)

宏va_arg展开后是关于下一个参数的类型和值的表达式,参数type是明确的类型名。

va_arg返回参数列表中的当前参数并使ap指向参数列表中的下一个参数。 

void va_end(va_list ap)

每次调用va_start就必须相应的调用va_end销毁变量ap,即将指针ap置为NULL。

void va_copy(va_list dest, va_list src)

复制va_list类型的变量。

每次调用va_copy,也必须有相应的va_end调用。

调用者在实际调用参数个数可变的函数时,要通过一定的方法指明实际参数的个数,例如把最后一个参数置为空字符串(系统调用execl()就是这样的)、-1或其他的方式(函数printf()就是通过第一个参数,即输出格式的定义来确定实际参数的个数的)。 

3. 举例:

  #include <iostream.h>

  #include <stdarg.h>

 

int main()

{int a,b,c,d,e;

int max(int,int...);

 cin>>a>>b>>c>>d>>e;

 cout<<"The bigger between a and b is "<<max(2,a,b)<<endl;

 cout<<"The bigger in the five number is "<<max(5,a,b,c,d,e)<<endl;

return 0;

}

 

int max(int num,int integer...)

{ va_list ap;

  int m=integer;

  va_start(ap,integer);

  for(int i=1;i<num;i++)

   { int t=va_arg(ap,int);

     if (t>m)  m=t;

 cout<<i<<endl;

   }

   va_end(ap);

   return m;

 }

 

 

 

下面是 <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,应该养成获取完参数表之后关闭指针的习惯。
  例如开始的例子 int max(int n, ...); 其函数内部应该如此实现:
int max(int n, ...) {                   // 定参 n 表示后面变参数量,定界用,输入时切勿搞错
  va_list ap;                            // 定义一个 va_list 指针来访问参数表
  va_start(ap, n);                       // 初始化 ap,让它指向第一个变参
  int maximum = -0x7FFFFFFF;            // 这是一个最小的整数
  int temp;
  for(int i = 0; i < n; i++) {
    temp = va_arg(ap, int);             // 获取一个 int 型参数,并且 ap 指向下一个参数
    if(maximum < temp) maximum = temp;
  }
  va_end(ap);                            // 善后工作,关闭 ap
  return max;
}
// 在主函数中测试 max 函数的行为(C++ 格式)
int main() {
  cout << max(3, 10, 20, 30) << endl;
  cout << max(6, 20, 40, 10, 50, 30, 40) << endl;
}
  基本用法阐述至此,可以看到,这个方法存在两处极严重的漏洞:其一,输入参数的类型随意性,使得参数很容易以一个不正确的类型获取一个值(譬如输入一个float,却以int型去获取他),这样做会出现莫名其妙的运行结果;其二,变参表的大小并不能在运行时获取,这样就存在一个访问越界的可能性,导致后果严重的 RUNTIME ERROR。
下面是 <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,应该养成获取完参数表之后关闭指针的习惯。
  例如开始的例子 int max(int n, ...); 其函数内部应该如此实现:
int max(int n, ...) {                   // 定参 n 表示后面变参数量,定界用,输入时切勿搞错
  va_list ap;                            // 定义一个 va_list 指针来访问参数表
  va_start(ap, n);                       // 初始化 ap,让它指向第一个变参
  int maximum = -0x7FFFFFFF;            // 这是一个最小的整数
  int temp;
  for(int i = 0; i < n; i++) {
    temp = va_arg(ap, int);             // 获取一个 int 型参数,并且 ap 指向下一个参数
    if(maximum < temp) maximum = temp;
  }
  va_end(ap);                            // 善后工作,关闭 ap
  return max;
}
// 在主函数中测试 max 函数的行为(C++ 格式)
int main() {
  cout << max(3, 10, 20, 30) << endl;
  cout << max(6, 20, 40, 10, 50, 30, 40) << endl;
}
  基本用法阐述至此,可以看到,这个方法存在两处极严重的漏洞:其一,输入参数的类型随意性,使得参数很容易以一个不正确的类型获取一个值(譬如输入一个float,却以int型去获取他),这样做会出现莫名其妙的运行结果;其二,变参表的大小并不能在运行时获取,这样就存在一个访问越界的可能性,导致后果严重的 RUNTIME ERROR。

分享到:
评论

相关推荐

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

    ### C语言可变参数机制详解:`va_list`与`vsnprintf`及`printf`实现 #### 引言 在C语言编程中,我们经常使用`printf`函数来输出各种格式的数据,但你是否曾思考过,这个看似简单的函数背后隐藏着怎样的复杂性?`...

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

    - **`va_arg(ap, t)`**:从可变参数列表中获取下一个参数,并更新`va_list`类型的指针`ap`,使其指向下一个参数。这里的`t`是指定的参数类型。 - **`va_end(ap)`**:将`va_list`类型的指针`ap`设置为无效状态,通常...

    C语言可变参数_va_list和_vsnprintf及printf实现.

    本文详细介绍了C语言中可变参数函数的实现机制,包括`_va_list`的使用方法,以及如何基于这些机制构建自己的可变参数函数,如简易的`printf`函数。通过理解`_va_list`和`vsnprintf`等函数的工作原理,开发者可以更好...

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

    1. **定义`va_list`类型的变量**:在函数内部定义一个`va_list`类型的变量,用于保存指向可变参数的指针。 2. **初始化`va_list`变量**:使用`va_start`宏初始化`va_list`类型的变量。这一步非常重要,因为它告诉...

    C51可变参数讲解.pdf

    可变参数函数的实现主要包括几个宏:va_list, va_start, va_arg, va_end。下面将详细讲解这些宏以及它们的用法: 1. va_list: 这个类型用于声明一个变量,该变量会被用来遍历参数列表。在stdarg.h中,va_list被定义...

    va-list原理解释

    2. 然后用VA_START宏初始化变量刚定义的VA_LIST变量,这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数。 3. 然后用VA_ARG宏返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型。 4. 最后...

    va_list(),va_start(),va_arg(),va_end() 详细解析

    写可变参数的C函数要在程序中用到以下这些宏: 代码如下:void va_start( va_list arg_ptr, prev_param );type va_arg( va_list arg_ptr, type );void va_end( va_list arg_ptr );va在这里是variable-argument(可变...

    详解-C语言可变参数-va-list和-vsnprintf及printf实现.pdf

    C语言可变参数实现机制详解 ...我们可以通过 va_list 和 vsnprintf 等机制来实现可变参数的函数,并且可以通过栈操作来确定可变参数的位置。但是,这只是一个简单的示例代码,实际上还需要根据具体情况进行调整和优化。

    va_arg_c_

    在C语言中,`va_arg`是一个非常重要的函数宏,它是可变参数列表(Variable Argument List,也称为 variadic function)处理的关键组成部分。标题“va_arg_c_”可能指的是一个文档,专门探讨了如何在C语言中使用`va_...

    C语言中一些可变参数的用法

    本文讨论了C语言中可变参数的用法,包括如何写一个简单的可变参数的C函数,如何使用可变参数宏va_start、va_arg和va_end,以及可变参数在编译器中的处理。 一、写一个简单的可变参数的C函数 在C语言中,某些函数...

    关于C/C++中可变参数的详细介绍(va_list,va_start,va_arg,va_end)

    在使用可变参数之前,你需要定义一个`va_list`类型的变量,例如`va_list arg_ptr`,用来存储和遍历可变参数列表。 2. `va_start`: `va_start`宏用于初始化`va_list`指针,使其指向可变参数列表的开始。它的两个...

    test_va_args

    这通常涉及到函数的可变参数(Variable Argument List,简称va_list)技术。在C和C++语言中,`test_va_args`是一个常见的示例,用于演示如何在函数中处理可变数量的参数。本文将深入探讨这个问题,讲解如何在数据...

    关于可变参数va系列函数用法的几个例子

    这里的`ap`是一个指向可变参数列表的指针(通常是一个`va_list`类型的变量),`last_fixed_arg`是可变参数列表前的最后一个固定参数。`va_start`宏会将`ap`指向`last_fixed_arg`后面的第一个可变参数。 例如,我们...

    c语言实现的一个简单的日志函数

    这个头文件中定义了`va_list`、`va_start`、`va_arg`和`va_end`宏,它们是处理可变参数列表的关键。下面逐一解释这些宏的作用: 1. `va_list`: 这是一个类型定义,用来声明一个可变参数列表的变量。例如:`va_list ...

    C语言中的可变参数

    `va_arg(args, type)` 用于从可变参数列表中获取下一个参数,其中 `args` 是 `va_list` 类型的变量,`type` 是要获取的参数类型。注意,每次调用 `va_arg` 时,都会自动移动 `args` 指向下一个参数,因此需要按照...

    C语言可变参数技术内幕.pdf

    va_arg宏的原型为:type va_arg(va_list ap, type),其中ap是指向可变参数表的指针,type是可变参数的类型。va_end宏的原型为:void va_end(va_list ap),其中ap是指向可变参数表的指针。 二、C语言可变参数技术的...

    C语言中可变参数的用法

    在C语言中,通过宏`va_start`、`va_arg`和`va_end`来处理这些可变参数。这些宏定义在`stdarg.h`头文件中,因此编写可变参数函数时需要包含此头文件。 #### 宏定义解析 1. **`va_start`**: 初始化可变参数列表的...

    C语言可变参数的使用

    这个头文件包含了一些关键的函数和类型定义,如`va_list`、`va_start`、`va_arg`和`va_end`,它们是处理可变参数的关键。 1. `va_list`:这是一个类型定义,用于存储可变参数列表的指针。通常我们会声明一个`va_...

Global site tag (gtag.js) - Google Analytics