历史上的今天
今天是:2024年10月13日(星期日)
2021年10月13日 | S3C2440裸机------从零实现用于裸机调试的printf函数
2021-10-13 来源:eefocus
我们的C语言中有标准的printf,可以很方便的打印一些变量的值用于调试,在嵌入式开发中,我们通过串口实现我们自己的printf函数,将一些变量值通过串口打印出来。
1.printf函数介绍
首先看一下C语言中的printf函数中的格式字符,我们就是要实现下表中的几个格式字符。

然后看一下printf函数的声明,int printf(const char * format, ...);
format: 表示固定参数,
...: 表示可变参数
2.手动确定可变参数
要实现printf函数,首先我们要知道怎么取到format和...所接受到的参数,我们取参数的依据就是:x86平台,函数调用时参数传递是使用堆栈来实现的,我们的printf的第一个参数是format,然后第二个参数的地址就是&format + sizeof(char *),具体原理如下 。

代码如下:
/*
* push_test.c V1.0
* Copyright (c) 2017 Shenzhen 100ask Technology Co.Ltd.All rights reserved.
* http://www.100ask.org
* 100ask.taobao.com
*
* 测试平台: ubuntu16.04(64位机器)
* ubuntu9.10 (32位机器)
* 编译器 : gcc
*/
#include struct person{ char *name; int age; char score; int id; }; /* *int printf(const char *format, ...); *依据:x86平台,函数调用时参数传递是使用堆栈来实现的 *目的:将所有传入的参数全部打印出来 */ int push_test(const char *format, ...) { char *p = (char *)&format; int i; struct person per; char c; double d; printf("arg1 : %sn",format); //============== /*指针对连续空间操作时: 1) 取值 2)移动指针*/ p = p + sizeof(char *); i = *((int *)p); printf("arg2 : %dn",i); //============== /*指针对连续空间操作时: 1) 取值 2)移动指针*/ p = p + sizeof(int); per = *((struct person *)p); printf("arg3: .name = %s, .age = %d, .socre=%c .id=%dn", per.name, per.age, per.score, per.id); //============== /*指针对连续空间操作时: 1) 取值 2)移动指针*/ p = p + sizeof(struct person); c = *((char *)p); printf("arg4: %cn",c); //============== /*指针对连续空间操作时: 1) 取值 2)移动指针*/ p = p + ((sizeof(char) + 3) & ~3); d = *((double *)p); printf("arg5: %fn",d); return 0; } int main(int argc,char **argv) { struct person per={"www.100ask.org",10,'A',123}; printf("sizeof(char )=%dn",sizeof(char )); printf("sizeof(int )=%dn",sizeof(int )); printf("sizeof(char *)=%dn",sizeof(char *)); printf("sizeof(char **)=%dn",sizeof(char **)); printf("sizeof(struct person)=%dn",sizeof(struct person)); //push_test("abcd"); //push_test("abcd",123); //push_test("abcd",123,per); //push_test("abcd",123,per,'c'); push_test("abcd",123,per,'c',2.79); return 0; } 3.自动确定可变参数 上面在修改指针的值以及取值是手动操作的,其实我们可以通过使用宏定义将修改指针的值和取值做成一个循环。 代码如下: /* * push_test.c V1.0 * Copyright (c) 2017 Shenzhen 100ask Technology Co.Ltd.All rights reserved. * http://www.100ask.org * 100ask.taobao.com * * 测试平台: ubuntu16.04(64位机器) gcc -m32 -o push_test push_test.c * ubuntu9.10 (32位机器) gcc -o push_test push_test.c * 编译器 : gcc */ #include //#include //下面这几个关于va的宏定义可以在stdarg.h中找到。 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_arg(ap,t) (ap = ap + _INTSIZEOF(t), *(t *)(ap - _INTSIZEOF(t))) #define va_arg(ap,t) (*(t *)(ap = ap + _INTSIZEOF(t), ap - _INTSIZEOF(t))) #define va_end(ap) ( ap = (va_list)0 ) struct person{ char *name; int age; char score; int id; }; /* *int printf(const char *format, ...); *依据:x86平台,函数调用时参数传递是使用堆栈来实现的 *目的:将所有传入的参数全部打印出来 */ int push_test(const char *format, ...) { //char *p = (char *)&format; int i; struct person per; char c; double d; va_list p; printf("arg1 : %sn",format); //============== //p = p + sizeof(char *); va_start(p, format ); /*指针对连续空间操作时: 1) 取值 2)移动指针*/ //i = *((int *)p); //p = p + sizeof(int); i = va_arg(p,int); printf("arg2 : %dn",i); //============== /*指针对连续空间操作时: 1) 取值 2)移动指针*/ //per = *((struct person *)p); //p = p + sizeof(struct person); per = va_arg(p,struct person); printf("arg3: .name = %s, .age = %d, .socre=%c .id=%dn", per.name, per.age, per.score, per.id); //============== /*指针对连续空间操作时: 1) 取值 2)移动指针*/ //c = *((char *)p); //p = p + ((sizeof(char) + 3) & ~3); c = va_arg(p,int); printf("arg4: %cn",c); //============== /*指针对连续空间操作时: 1) 取值 2)移动指针*/ //d = *((double *)p); //p = p + sizeof(double); d = va_arg(p,double); /*避免"野指针"*/ //p = (char *)0; va_end( p ); printf("arg5: %fn",d); return 0; } int main(int argc,char **argv) { struct person per={"www.100ask.org",10,'A',123}; printf("sizeof(char )=%dn",sizeof(char )); printf("sizeof(int )=%dn",sizeof(int )); printf("sizeof(char *)=%dn",sizeof(char *)); printf("sizeof(char **)=%dn",sizeof(char **)); printf("sizeof(struct person)=%dn",sizeof(struct person)); //push_test("abcd"); //push_test("abcd",123); //push_test("abcd",123,per); //push_test("abcd",123,per,'c'); push_test("abcd",123,per,'c',2.79); return 0; } 4.printf函数在x86平台的实现 直接看代码:: my_printf.h #ifndef _MY_PRINTF_H #define _MY_PRINTF_H //#include "uart.h" #include #define __out_putchar putchar #define MAX_NUMBER_BYTES 64 extern int my_printf_test(void); int printf(const char *fmt, ...); #endif /* _MY_PRINTF_H */ my_printf.c #include "my_printf.h" //================================================================================================== 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_arg(ap,t) ( *(t *)( ap=ap + _INTSIZEOF(t), ap- _INTSIZEOF(t)) ) #define va_end(ap) ( ap = (va_list)0 ) //================================================================================================== unsigned char hex_tab[]={'0','1','2','3','4','5','6','7', '8','9','a','b','c','d','e','f'}; static int outc(int c) { __out_putchar(c); return 0; } static int outs (const char *s) { while (*s != '




