文档介绍:C 语言的变长参数在平时做开发时很少会在自己设计的接口中用到,但我们最常用的接口
printf 就是使用的变长参数接口,在感受到 printf 强大的魅力的同时,是否想挖据一下到底
printf 是如何实现的呢fmt, ... )
{
char *ap;
ap = ((char*)&fmt) + sizeof(fmt);
printf("%d\n", *(int*)ap);
ap = ap + sizeof(int);
printf("%d\n", *(int*)ap);
ap = ap + sizeof(int);
printf("%s\n", *((char**)ap));
}
int main()
{
var_args_func("%d %d %s\n", 4, 5, "hello world");
}
输出结果:
4
5
hello world
var_args_func只是为了演示,并未根据fmt消息中的格式字符串来判断变参的个数和类型,
而是直接在实现中写死了,如果你把这个程序拿到solaris 9 下,运行后,一定得不到正确的
结果,为什么呢,后续再说。先来解释一下这个程序。我们用ap获取第一个变参的地址,我
们知道第一个变参是 4,一个int型,所以我们用(int*)ap以告诉编译器,以ap为首地址的那块
内存我们要将之视为一个整型来使用,*(int*)ap获得该参数的值;接下来的变参是 5,又一
个int型,其地址是ap + sizeof(第一个变参),也就是ap + sizeof(int),同样我们使用*(int*)ap
获得该参数的值;最后的一个参数是一个字符串,也就是char*,与前两个int型参数不同的
是,经过ap + sizeof(int)后,ap指向栈上一个char*类型的内存块(我们暂且称之tmp_ptr, char*tmp_ptr)的首地址,即ap -> &tmp_ptr,而我们要输出的不是printf("%s\n", ap),而是
printf("%s\n", tmp_ptr); printf("%s\n", ap)是意图将ap所指的内存块作为字符串输出了,但是ap
-> &tmp_ptr,tmp_ptr所占据的 4 个字节显然不是字符串,而是一个地址。如何让&tmp_ptr
是char **类型的,我们将ap进行强制转换(char**)ap <=> &tmp_ptr,这样我们访问tmp_ptr只
需要在(char**)ap前面加上一个*即可,即printf("%s\n", *(char**)ap);
前面说过,如果将var_args_func放到solaris上,一定是得不到正确结果的?为什么呢?由于内
存对齐。编译器在栈上压入参数时,不是一个紧挨着另一个的,编译器会根据变参的类型将
其放到满足类型对齐的地址上的,这样栈上参数