Lesson-18.md

December 17, 2012 · View on GitHub

Lesson 18 My printf 实现 printf

#include <stdio.h>

void putchar_hex(char c)
{
	const char * hex = "0123456789ABCDEF";  // good
	//char hex[] = "0123456789ABCDEF";  bad!

	putchar(hex[(c & 0xf0)>>4]);
	putchar(hex[(c & 0x0f)>>0]);
	//putchar(' ');
}

void putint_hex(int a)
{
	putchar_hex( (a>>24) & 0xFF );
	putchar_hex( (a>>16) & 0xFF );
	putchar_hex( (a>>8) & 0xFF );
	putchar_hex( (a>>0) & 0xFF );
}

char * itoa(int num, char * buf)
{   
	int i = 0;
	int len = 0;

	do 
	{
		buf[i++] = num % 10 + '0';
		num /= 10;      
	} while (num);
	buf[i] = '\0';

	len = i;
	for (i = 0; i < len/2; i++)
	{
		char tmp;
		tmp = buf[i];
		buf[i] = buf[len-i-1];
		buf[len-i-1] = tmp;
	}

	return buf; 
}

static int myputs(const char * s)
{
	while (*s)
		putchar(*s++);

	return 0;
}
		
#if 0
#include <stdarg.h>
#else
typedef int * va_list;
#define va_start(ap, A)      (ap = (int *)&(A) + 1)
#define va_arg(ap, T)        (*(T *)ap++)
#define va_end(ap)       ((void)0)
#endif	

static char buf[100]; 

int myprintf(const char * format, ...)
{
	char c;

	va_list ap;

	va_start(ap, format);

	while ((c = *format++) != '\0')
	{
		switch (c)
		{
			case '%':
				c = *format++;

				switch (c)
				{
					char ch;
					char * p;
					int a;

					case 'c':
						ch = va_arg(ap, int);
						putchar(ch);
						break;
					case 's':
						p = va_arg(ap, char *);
						myputs(p);
						break;                  
					case 'x':
						a = va_arg(ap, int);
						putint_hex(a);
						break;      
					case 'd':
						a = va_arg(ap, int);
						itoa(a, buf);
						myputs(buf);
						break;  
					default:
						break;
				}               
				break;      

			default:
				putchar(c);
				break;
		}
	}

	return 0;   
}

int main(void)
{
	myprintf("test: %c, %s, %d, 0x%x\n", 'A', "abcdef", 11, 0x23);

	return 0;
}

语法知识点

  • printf 的函数原型
  • typedef 类型声明的用法
  • va_start, va_arg, va_end 3个宏的实现
  • switch-case 语句的嵌套用法
  • 指针的指针 用法

课堂讨论

  • printf 的原型,参数和返回值各是什么?
  • 为何用 %c 打印时,传入 my_va_arg 的参数是一个 int 类型?
  • 当前栈帧的位置是如何判别的? 它和哪些因素有关?

课后练习

  • 请实现一个可变长度参数的 max 函数,可以求出任意多传入参数中的最大值。
    例如 max(3, 100, 200, 300) 返回 300; max(6, 10, 20, 30, 40, 50, 60) 返回 60
  • 请在 myprintf 的基础上,实现一个 mysprintf 函数。