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宏。