- 浏览: 87582 次
- 性别:
- 来自: 昆明
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的介绍。
本文主要介绍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的介绍。
发表评论
-
HOW TO ADD PHOTOS TO THE IPHONE SIMULATOR
2012-12-25 15:49 735Building an app that needs to a ... -
截取部分图片并显示
2012-09-14 11:15 828src : http://marshal.easymorse ... -
iPhone/iPad全屏截图与区域截图的几种方法
2012-09-06 13:48 4193http://www.cocoachina.com/newbi ... -
【转载】将int型数据转换成任意进制字符串的算法
2012-08-28 09:50 7271. http://hi.baidu.com/doking_b ... -
iOS 使用 predicate 限定 NSNumber 类型的数据
2012-07-02 13:25 900错误的写法: predicate = [NSPredicat ... -
在Mac OS X Lion系统中访问~/Library目录都需要点技巧
2012-07-02 10:52 1008Mac虚拟机升级为Lion系统了,在iPhone模拟机 ... -
XCode调试 设置全局断点并快速定位问题代码所在行
2012-06-20 19:17 0http://www.kaifazu.com/iOS_kfjc ... -
Error Domain=NSOSStatusErrorDomain Code=-9807
2012-06-14 10:28 6839Client 端连接服务器时会有时会遇到一下错误: ... -
iOS 的 keychain 简介
2012-05-03 10:38 1226src: http://www.cnblogs.com/v2m ... -
iOS 监听App音量的变化
2012-03-31 18:02 5504方法1: 在applicationDidFinish ... -
Exception and Signal
2012-03-27 15:11 641src: http://publib.boulder.ibm. ... -
Handling unhandled exceptions and signals
2012-03-27 14:54 734src: http://cocoawithlove.com/2 ... -
操作CoreData 常见的错误及解决方法
2012-03-07 18:06 789src: http://blog.csdn.net/ch_ ... -
scrollViewDidScroll 和scrollViewDidEndScrollingAnimation的区别
2012-02-23 11:30 4395UIScrollViewDelegate has got ... -
iOS自定义风火轮UIActivityIndicator
2012-02-15 23:07 6543src:http://blog.csdn.net/kmyhy/ ... -
Google Talk 和 Google Voice 的终极整合
2012-02-15 10:14 812src : http://dan.febird.net/2 ... -
How To Use UIView Animation Tutorial
2012-02-08 16:20 722src: http://www.raywenderlich.c ... -
iOS应用程序状态切换相关
2012-01-31 15:14 834原文出处: http://blog.csdn.net/duan ... -
xcode4 设置调试错误信息小结
2012-01-17 13:17 900原文出处: http://blog.csdn.net/coc ... -
抓包工具charles使用方法
2012-01-08 14:08 3913这个是charles在mac上的使用方法 http://www ...
相关推荐
### stdarg.h中三个宏va_start,va_arg和va_end的应用详解 在C语言中,经常需要编写能够接受可变数量参数的函数。一个典型的例子就是`printf`函数,它可以接受任意数量和类型的参数。为了支持这种功能,C语言提供了...
### va_start、va_end、va_list的详细使用方法 #### 概述 在C语言中,函数参数的数量通常是固定的。然而,在某些情况下,我们可能希望一个函数能够接收可变数量的参数。例如,`printf`函数就是一个很好的例子,它...
这种灵活性主要由两个关键组件实现:`va_list`类型和宏`va_start`, `va_arg`和`va_end`。下面我们将逐一解析这些概念。 **`va_list`** `va_list`是一个特殊类型,用于存储可变参数列表的状态。当函数被调用时,`va...
这个示例中,我们可以看到 printf 函数使用 va_list 来存储参数,并使用 va_start 和 va_arg 宏来访问参数。 四、printf 函数的实现流程 printf 函数的实现流程可以分为以下几个步骤: 1. va_start(ap, format); ...
通过使用`va_list`、`va_start`、`va_arg`和`va_end`宏,程序员可以构建能够适应各种输入场景的函数。然而,值得注意的是,由于这些宏直接操作栈上的数据,不当使用可能会导致栈溢出或其他运行时错误。因此,在设计...
在C语言中实现可变参数函数的关键在于`stdarg.h`头文件中的几个宏:`va_list`、`va_start`、`va_arg`和`va_end`。首先,`va_list`定义了一个类型,用于存储可变参数列表的指针。接着,`va_start`宏初始化`va_list`,...
通过以上示例,我们可以看到,在C/C++中处理变长参数时,通常会涉及到`va_list`、`va_start`、`va_arg`和`va_end`这几个关键概念。在实际应用中,还需要注意以下几点: 1. **类型安全**:变长参数列表不进行类型...
va_start和va_end使用详解 Keil中使用宏编译来定义DEBUG输出 typedef void (*func)(void *) 的含义 快速运算之:使用与运算符代替求余运算符的技巧 多文件目录下makefile文件递归执行编译所有c文件 STM32F1做RSA,...
- **资源管理**:正确使用 `va_start()` 和 `va_end()` 宏,确保资源得到妥善管理。 #### 六、总结 `vsprintf()` 函数提供了强大的字符串格式化功能,特别适用于需要动态参数的情况。通过合理使用 `va_list` 和相关...
可变参数函数的核心在于`va_list`、`va_start`、`va_arg`和`va_end`这几个宏的使用。下面分别解释这些宏的作用: 1. **va_list**: 用于声明一个可变参数列表。 2. **va_start**: 初始化可变参数列表。 3. **va_arg*...
C++标准库提供了`stdarg.h`头文件,其中包含的`va_list`、`va_start`、`va_arg`和`va_end`宏可用于处理可变参数列表。通过这些宏,我们可以创建一个函数或方法,该函数能够接受任意数量和类型的参数。这使得我们的...
这些宏包括va_list、va_start、va_arg和va_end等。 stddef.h:公共定义 stddef.h头文件提供了一组公共定义,用于定义一些基本的数据类型和宏。这些定义包括NULL、offsetof和ptrdiff_t等。 stdio.h:输入输出 ...
2. `va_start`: 这个宏用于初始化`va_list`变量`ap`,以便后续使用`va_arg`和`va_end`。它的第二个参数`last`是可变参数列表之前最后一个已知类型参数的名字。 3. `va_arg`: 这个宏用于从参数列表中获取下一个参数...
描述“改变字体颜色 c语言 UNIX linux”进一步强调了本文将关注于使用C语言,在UNIX和Linux系统中实现字体颜色的变化。 #### 知识点详述 1. **ANSI转义序列基础** 在UNIX和Linux系统中,可以通过ANSI(American ...
7. **变长参数(Variable Length Argument List)**:使用`...`来表示函数可以接受任意数量的参数,通常与`va_list`, `va_start`, `va_arg`和`va_end`宏一起使用,但这种做法不常见,且不利于编译器进行类型检查。...
### printf函数族用法详解 #### 一、概述 `printf`函数族是C语言中极为重要的函数之一,主要用于格式化输出数据。该家族包括多种不同的函数,它们各自有不同的应用场景和特点。本篇文章将详细介绍`printf`函数族的...
例如,`void print(int count, ...)`可以接收任意数量的参数,通过`va_list`, `va_start`, `va_arg`和`va_end`进行处理。 学习C语言需要理解这些核心概念,并通过实践加深理解。对于初学者,指针可能是一项挑战,但...
#### 二、open系统函数实现详解 本节将深入探讨Linux内核中`open`系统调用的具体实现细节,包括其源码解析以及宏定义展开过程。 ##### 1.1 函数实现 `open`系统调用的主要实现在`fs/open.c`文件中,具体实现如下...
`va_list`, `va_start()`, `va_arg()`, `va_end()`等宏定义了处理可变参数的方法。 10. `<stddef.h>`:公共定义 定义了一些标准的类型,如`size_t`用于表示内存大小,`ptrdiff_t`用于表示两个指针间的差异,以及`...