C语言中的变参原理

在C/C++中,对函数参数的扫描是从后向前的。C/C++的函数参数是通过压入堆栈的方式来给函数传参数的(堆栈是一种先进后出的数据结构),最先压入的参数最后出来printf的第一个被找到的参数就是那个字符指针,就是被双引号括起来的那一部分,函数通过判断字符串里控制参数的个数来判断参数个数及数据类型,通过这些就可算出数据需要的堆栈指针的偏移量了。
参数是最后的先压入栈中,最先的后压入栈中,参数控制的那个字符串常量是最后被压入的,所以这个常量总是能被找到的。

C语言的函数是从下(低地址)向上(高地址)压入堆栈的,如下图所示:
栈底 高地址
| …
| 函数返回地址
| …
| 函数最后一个参数
| …
| 函数第一个可变参数 <–va_start后ap指向
| 函数最后一个固定参数
| …
| 函数第一个固定参数
栈顶 低地址

实现变参的关键数据结构:
typedef int * va_list;
//va_list等价于int *即整型指针,该变量类型应该根据具体的架构(ARM,X86)确定
#define va_start(ap, A) (ap = (int *)&(A) + 1)
//(int *)&得到A所在的地址,并强制类型转换为int ,然后这个地址加上A的大小,则使ap指向第一个可变参数!!
#define va_arg(ap, T) (
(T *)ap++)
//先对指针ap(即地址)进行强制类型转换,转换为该变量实际的类型, 然后ap(即当前地址)自加1(即加一个类型的大小,指向下一个可变参数的地址),这类应该注意的是,先使用后自加,然后取出该地址的值!!
#define va_end(ap) ((void)0)

精简版prinf的例子:

#define va_start(ap, A)		(ap = (int *)&(A) + 1)
#define va_arg(ap, T)		(*(T *)ap++)
#define va_end(ap)		((void)0)

int printf(const char * format, ...)
{
	char c;	
	va_list ap; //定义一个int *型的指针ap	
	va_start(ap, format); //初始化ap,让它指向第一个可变参数的地址 
	
	while ((c = *format++) != '\0') //开始解析printf函数中的字符串(即第一个固定参数)
	{
		switch (c) //在while中,依次读出字符串中的字符,在这里依依地进行判断和解析
		{
			case '%': //如果字符是%,即格式申明符号,则解析该格式
				c = *format++; //让c等于%后的第一个字母
				switch (c) //对上述字母进行解析
				{
					char ch;
					char * p;
					int a;
					char buf[100];
									
					case 'c': //如果是%c,即输出字母
						ch = va_arg(ap, int); //获取当前可变参数的值,并指向下一个可变参数
						putchar(ch); //调用底层(实际操作硬件的那层)来完成输出结果
						break;
					case 's': //如果是%s,即输出字符串
						p = va_arg(ap, char *);
						puts(p);
						break;					
					case 'x': //如果是%x,即以十六进制输出
						a = va_arg(ap, int);
						putint_hex(a);
						break;		
					case 'd': //如果是%d,即以十进制输出
						a = va_arg(ap, int);
						itoa(a, buf);
						puts(buf);
						break;	
					
					default:
						break;
				}				
				break;		
		
			default:
				putchar(c); //输出字符串中为普通字符的字符
				break;
		}
	}
	return 0;	
} 

实际上就是在第一个固定参数里面解析出来各种字符输出控制变量,然后根据变量类型去栈中偏移相应的地址找到参数实际值,然后根据不同格式调用不同的输出函数即可完成变参显示printf函数。

发布了27 篇原创文章 · 获赞 18 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_24622489/article/details/86649502
今日推荐