对C语言中va_list,va_start,va_arg和va_end的一点理解

  这几个函数和变量是针对可变参数函数的,什么是可变参数函数呢,最经典的莫过于printf和scanf,这两个函数的声明如下:

1 int printf(const char *format, ...);
2
3 int scanf(const char *format, ...);

  这两个函数声明中省略号(...)表示的就是任意个数的参数,可变参数函数就是输入的参数的个数是可变的,那么这个具体是怎么实现的呢?

  要了解这个是怎么实现,首先我们就要先理解一点,参数是如何传递给函数的。众所周知,函数的数据是存放于栈中的,那么给一个函数传递传递参数的过程就是将函数的参数从右向左逐次压栈,例如:

  func(int i, char c, doube d)

  这个函数传递参数的过程就是将d,c,i逐次压到函数的栈中,由于栈是从高地址向低地址扩展的,所以d的地址最高,i的地址最低。

  理解了函数传递参数的过程,再来说一下va_list的原理,通常,可变参数的代码是这么写的:

1 void func(char *fmt, ...)
2 {
3      va_list ap;
4
5      va_start(ap, fmt);
6      va_arg(ap, int);
7      va_end(va);
8 }

  这里ap其实就是一个指针,指向了参数的地址。

  va_start()所做的就是让ap指向函数的最后一个确定的参数(声明程序中是fmt)的下一个参数的地址。

  va_arg()所做的就是根据ap指向的地址,和第二个参数所确定的类型,将这个参数的中的数据提取出来,作为返回值,同时让ap指向下一个参数。

  va_end()所做的就是让ap这个指针指向0。

  关于这三个参数实现的宏可以参看下面的实现:

1 // 使ap指向第一个可变参数的地址
2 #define  va_start(ap,v)     ( ap = (va_list)&v + _INTSIZEOF(v) )
3
4 // 使ap指向下一个可变参数,同时将目前ap所指向的参数提取出来并返回
5 #define  va_arg(ap,t)       ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
6
7 //  销毁ap
8 #define  va_end(ap)         ( ap = (va_list)0 ) 

参考文章:

1. 详解 C语言可变参数 va_list和_vsnprintf及printf实现

时间: 2024-09-29 22:58:09

对C语言中va_list,va_start,va_arg和va_end的一点理解的相关文章

va_list & va_start & va_arg & va_end

va_list 属于变量 而 va_start & va_arg & va_end  C语言中解决变参问题的一组宏.头文件来自stdarg.h. 查看linux系统源码方式我一般用locate stdarg.h,然后找到提示目录vi进去.源文件定义是: 在Mac 下追踪头文件也会发现如下宏定义: typedef __darwin_va_list va_list; typedef __builtin_va_list    __darwin_va_list;    /* va_list */

va_list/va_start/va_arg/va_end深入分析

va_list/va_start/va_arg/va_end深入分析 va_list/va_start/va_arg/va_end这几个宏,都是用于函数的可变参数的. 我们来看看在vs2008中,它们是怎么定义的: 1: ///stdarg.h 2: #define va_start _crt_va_start 3: #define va_arg _crt_va_arg 4: #define va_end _crt_va_end 5: 6: ///vadefs.h 7: #define _ADD

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

可变参数的函数原理其实很简单,而va系列是以宏定义来定义的,实现跟堆栈相关.我们写一个可变函数的C函数时,有利也有弊,所以在不必要的场合,我们无需用到可变参数.如果在C++里,我们应该利用C++的多态性来实现可变参数的功能,尽量避免用C语言的方式来实现. 由于在C语言中没有函数重载,解决不定数目函数参数问题变得比较麻烦,即使采用C++,如果参数个数不能确定,也很难采用函数重载.对这种情况,提出了指针参数来解决问题. 如printf()函数,其原型为:int   printf(   const  

va_list 、va_start、 va_arg、 va_end 使用说明【转】

转自:https://blog.csdn.net/f110300641/article/details/83822290 在ANSI C中,这些宏的定义位于stdarg.h中: typedef char *va_list; va_start宏,获取可变参数列表的第一个参数的地址(list是类型为va_list的指针,param1是可变参数最左边的参数): #define va_start(list,param1)   ( list = (va_list)&param1+ sizeof(param

编写一个可变参数的C函数——头文件stdarg.h中宏va_start ,va_arg和va_end的应用

我们在C语言编程中会遇到一些参数个数可变的函数,例如printf()这个函数,它的定义是这样的:int printf( const char* format, ...);它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的,例如我们可以有以下不同的调用方法:printf("%d",i);printf("%s",s);printf("the number is %d ,string is:%s", i, s);究竟如何写可变参数的

va_start,va_arg,va_end的使用

一.在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表. void fun(...); void fun(parm_list,...); #include <stdio.h> void fun(int n,...) { int *temp=&n; temp++; for(int i=0;i<n;i++) { printf("%d\n",*temp); temp++; } } int main() { int a=1,b=2,c=3,

va_start、va_arg、va_end、va_copy 可变参函数

1.应用与原理 在C语言中又是我们无法给出一个函数参数的列表,比如: int printf(const char *format, ...); int fprintf(FILE *stream, const char *format, ...); 这是我们使用到了可以变参数,也就是使用...代表0个或多个参数. 那么编译器如何获取/使用这些参数.这涉及到参数的传递原理: 参数传递原理: 在内存中,函数的参数以栈的方式存取,从右到左入栈.这些参数存放的地址是连续的.这样,我们就可以通过获取第一个参

iOS va_list,va_start,va_end

- (id)initWithTitle:(NSString *)title message:(NSString *)message completionBlock:(void (^)(NSUInteger buttonIndex, EMAlertView *alertView))block cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... { /

va_list arg_list va_start(arg_list, format) va_end( arg_list ) 原理的理解

void log( int log_level, const char* file_name, int line_num, const char* format, ... ) { ............. .............. va_list arg_list; va_start( arg_list, format ); memset( arg_buffer, '\0', LOG_BUFFER_SIZE ); vsnprintf( arg_buffer, LOG_BUFFER_SIZE