快捷搜索:

编程入门:浅谈C语言的可变参数

C说话中有些函数应用可变参数,比如常见的int printf( const char* format, ...),第一个参数format是固定的,另外的参数的个数和类型都不固定。

C说话用va_start等宏来处置惩罚这些可变参数。这些宏看起来很繁杂,着实道理挺简单,便是根据参数入栈的特征从最接近第一个可变参数的固定参数开始,依次获取每个可变参数的地址。下面我们来阐发这些宏。

在stdarg.h头文件中,针对不合平台有不合的宏定义,我们拔取X86平台下的宏定义:

typedef char *va_list;

#define _INTSIZEOF(n)( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

#define va_start(ap,v)( ap = (va_list)&v + _INTSIZEOF(v) )

#define va_arg(ap,t)( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

#define va_end(ap)( ap = (va_list)0 )

_INTSIZEOF(n)宏是为了斟酌那些内存地址必要对齐的系统,从宏的名字来应该是跟sizeof(int)对齐。一样平常的sizeof(int)=4,也便是参数在内存中的地址都为4的倍数。比如,假如sizeof(n)在1-4之间,那么_INTSIZEOF(n)=4;假如sizeof(n)在5-8之间,那么_INTSIZEOF(n)=8。

为了能从固定参数依次获得每个可变参数,va_start,va_arg充分使用下面两点:

1. C说话在函数调用时,先将着末一个参数压入栈

2. X86平台下的内存分配顺序是从高地址内存到低地址内存

高位地址

第N个可变参数

。。。

第二个可变参数

第一个可变参数? ap

固定参数? v

低位地址

由上图可见,v是固定参数在内存中的地址,在调用va_start后,ap指向第一个可变参数。这个宏的感化便是在v的内存地址上增添v所占的内存大年夜小,这样就获得了第一个可变参数的地址。

接下来,可以这样设想,假如我能确定这个可变参数的类型,那么我就知道了它占用了若干内存,依葫芦画瓢,我就能获得下一个可变参数的地址。

让我再来看看va_arg,它先ap指向下一个可变参数,然后减去当前可变参数的大年夜小即获得当前可变参数的内存地址,再做个类型转换,返回它的值。

要确定每个可变参数的类型,有两种做法,要么都是默认的类型,要么就在固定参数中包孕足够的信息让法度榜样可以确定每个可变参数的类型。比如,printf,法度榜样经由过程阐发format字符串就可以确定每个可变参数大年夜类型。

着末一个宏就简单了,va_end使得ap不再指向有效的内存地址。

看了这几个宏,不禁让我再次感慨,C说话太机动了,而且代码可以写得异常简洁,虽然无意偶尔候让人看得不是很明白,然则一旦明白 过来,你肯定会为它击掌喝彩!

其其实varargs.h头文件中定义了UNIX System V推行的va系列宏,而上面在stdarg.h头文件中定义的是ANSI C形式的宏,这两种宏是不兼容的,一样平常说来,我们应该应用ANSI C形式的va宏。

您可能还会对下面的文章感兴趣: