C++用省略符+指针实现printf

首先,printf() 是一个不定形参函数,这一点我们可以通过省略符(...)来实现。

其次,我们怎么获取到这些不定实参呢?看个例子:


void func(int a, ...)
{

}

int main()
{
	func(1, 2, 3);
	return 0;
}

这里定义了一个 func 函数,使用了省略符,然后在 main 方法中我们调用了它,并传入了3个实参1,2,3,在编译时会生成这个函数的实例。

在函数调用时,实参将会从右到左依次入栈,在内存栈中的地址是连续的,所以我们只要取得其中任意一个实参的地址,就能通过对指针操作从而获得栈中其他参数,下图是给出例子反汇编的一部分:


可以发现,在 func(1, 2, 3)被调用时,执行了push 3,push 2,push 1这3个汇编指令。


看完原理直接来读代码,先配个图:


#include<iostream>

typedef char byte;

int printf(char *str, ...)
{
	char buff[1024];					//用来存放格式化后的字符串
	const byte *stack_ptr = nullptr;	//定义一个 byte 型的指针,用来操作内存栈,每个内存单元为一个 byte
	int pos = 0;						//指向当前 buff '\0' 位置	

	stack_ptr = (byte*)&str;			//指向第一个参数的地址(5)
	stack_ptr += sizeof(char *);		//将 stack_ptr 移动 sizeof(char *) 个内存单元以指向第二个参数起始地址(9)

	for (char *ptr = str; *ptr; ptr++)  //遍历 str 找出所有 %d,%s
	{
		char ch = *ptr;			

		if (ch != '%')
		{
			buff[pos++] = ch;			//如果不是 '%' 则直接放入 buff 中并将 pos+1
			continue;
		}
		else
		{
			ch = *(++ptr);				//如果是 '%' 则取其后面的字符
			if (ch == 's')				//如果是 's' (%s)
			{
				const char *s = *(char**)stack_ptr;			//根据 %s 判断出此参数为 char* 类型,将 stack_ptr 转化为指向 char* 类型的指针(char**)并取其值
				memcpy(buff + pos, s, strlen(s));			//将此参数指向字符串加入 buff 之中
				pos += strlen(s);							//更新 pos

				stack_ptr += sizeof(char *);				//将 stack_ptr 移动sizeof(char *) 个内存单元以指向下一个参数起始地址(17)
			}
			else if (ch == 'd')
			{
				char num_s[25];								//根据 %d 判断出此参数为 int 类型
				itoa(*(int*)stack_ptr, num_s, 10);			//将 stack_ptr 转化为指向 int 类型的指针(int*)并取其值传给 itoa 转化为字符串保存至 num_s

				memcpy(buff + pos, num_s, strlen(num_s));   //将此字符串加入 buff 之中
				pos += strlen(num_s);						//更新 pos

				stack_ptr += sizeof(int);					//将 stack_ptr 移动sizeof(int) 个内存单元以指向下一个参数起始地址(13)
			}
		}
	}

	puts(buff);							//输出 buff
	return 0;
}

int main()
{
	auto s = "木头人";
	printf("%d %s\n", 123, s);

	system("pause");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值