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语言编程中,我们经常使用`printf`函数来输出各种格式的数据,但你是否曾思考过,这个看似简单的函数背后隐藏着怎样的复杂性?`...
- **`va_arg(ap, t)`**:从可变参数列表中获取下一个参数,并更新`va_list`类型的指针`ap`,使其指向下一个参数。这里的`t`是指定的参数类型。 - **`va_end(ap)`**:将`va_list`类型的指针`ap`设置为无效状态,通常...
本文详细介绍了C语言中可变参数函数的实现机制,包括`_va_list`的使用方法,以及如何基于这些机制构建自己的可变参数函数,如简易的`printf`函数。通过理解`_va_list`和`vsnprintf`等函数的工作原理,开发者可以更好...
1. **定义`va_list`类型的变量**:在函数内部定义一个`va_list`类型的变量,用于保存指向可变参数的指针。 2. **初始化`va_list`变量**:使用`va_start`宏初始化`va_list`类型的变量。这一步非常重要,因为它告诉...
可变参数函数的实现主要包括几个宏:va_list, va_start, va_arg, va_end。下面将详细讲解这些宏以及它们的用法: 1. va_list: 这个类型用于声明一个变量,该变量会被用来遍历参数列表。在stdarg.h中,va_list被定义...
2. 然后用VA_START宏初始化变量刚定义的VA_LIST变量,这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数。 3. 然后用VA_ARG宏返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型。 4. 最后...
写可变参数的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 等机制来实现可变参数的函数,并且可以通过栈操作来确定可变参数的位置。但是,这只是一个简单的示例代码,实际上还需要根据具体情况进行调整和优化。
在C语言中,`va_arg`是一个非常重要的函数宏,它是可变参数列表(Variable Argument List,也称为 variadic function)处理的关键组成部分。标题“va_arg_c_”可能指的是一个文档,专门探讨了如何在C语言中使用`va_...
本文讨论了C语言中可变参数的用法,包括如何写一个简单的可变参数的C函数,如何使用可变参数宏va_start、va_arg和va_end,以及可变参数在编译器中的处理。 一、写一个简单的可变参数的C函数 在C语言中,某些函数...
在使用可变参数之前,你需要定义一个`va_list`类型的变量,例如`va_list arg_ptr`,用来存储和遍历可变参数列表。 2. `va_start`: `va_start`宏用于初始化`va_list`指针,使其指向可变参数列表的开始。它的两个...
这通常涉及到函数的可变参数(Variable Argument List,简称va_list)技术。在C和C++语言中,`test_va_args`是一个常见的示例,用于演示如何在函数中处理可变数量的参数。本文将深入探讨这个问题,讲解如何在数据...
这里的`ap`是一个指向可变参数列表的指针(通常是一个`va_list`类型的变量),`last_fixed_arg`是可变参数列表前的最后一个固定参数。`va_start`宏会将`ap`指向`last_fixed_arg`后面的第一个可变参数。 例如,我们...
这个头文件中定义了`va_list`、`va_start`、`va_arg`和`va_end`宏,它们是处理可变参数列表的关键。下面逐一解释这些宏的作用: 1. `va_list`: 这是一个类型定义,用来声明一个可变参数列表的变量。例如:`va_list ...
`va_arg(args, type)` 用于从可变参数列表中获取下一个参数,其中 `args` 是 `va_list` 类型的变量,`type` 是要获取的参数类型。注意,每次调用 `va_arg` 时,都会自动移动 `args` 指向下一个参数,因此需要按照...
va_arg宏的原型为:type va_arg(va_list ap, type),其中ap是指向可变参数表的指针,type是可变参数的类型。va_end宏的原型为:void va_end(va_list ap),其中ap是指向可变参数表的指针。 二、C语言可变参数技术的...
在C语言中,通过宏`va_start`、`va_arg`和`va_end`来处理这些可变参数。这些宏定义在`stdarg.h`头文件中,因此编写可变参数函数时需要包含此头文件。 #### 宏定义解析 1. **`va_start`**: 初始化可变参数列表的...
这个头文件包含了一些关键的函数和类型定义,如`va_list`、`va_start`、`va_arg`和`va_end`,它们是处理可变参数的关键。 1. `va_list`:这是一个类型定义,用于存储可变参数列表的指针。通常我们会声明一个`va_...