實(shí)現(xiàn)自己的printf函數(shù)
掃描二維碼
隨時(shí)隨地手機(jī)看文章
在嵌入式開發(fā)中,常常會(huì)通過串口打印一些信息到PC終端,這就需要實(shí)現(xiàn)自己的printf函數(shù)。
我們先通過man 3 printf
來看一下printf函數(shù)的聲明: int printf(const char *format, ...);
format:固定參數(shù)
… ? :可變參數(shù)(變參)
下面解析printf函數(shù)的使用:
x86平臺(tái)VC6.0編譯器中,stdarg.h頭文件內(nèi)變參宏定義如下,本次實(shí)現(xiàn)也printf函數(shù)也采用此定義:
變參宏根據(jù)堆棧生長(zhǎng)方向和參數(shù)入棧特點(diǎn),從最靠近第一個(gè)可變參數(shù)的固定參數(shù)開始,依次獲取每個(gè)可變參數(shù)的地址。
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,?type)????(?*(type?*)((ap?+=?_INTSIZEOF(type))?-?_INTSIZEOF(type))?) #define?va_end(ap)?????????????(?ap?=?(va_list)0?)
各宏的含義如下:
①_INTSIZEOF
宏考慮到某些系統(tǒng)需要內(nèi)存地址對(duì)齊。從宏名看應(yīng)按照sizeof(int)即堆棧粒度對(duì)齊,即參數(shù)在內(nèi)存中的地址均為sizeof(int)=4的倍數(shù)。例如,若在1≤sizeof(n)≤4,則_INTSIZEOF(n)=4;若5≤sizeof(n)≤8,則_INTSIZEOF(n)=8。
為便于理解,簡(jiǎn)化該宏為:
#define?_INTSIZEOF(n)??((sizeof(n)?+?x)?&?~(x)) x?=?sizeof(int)?-?1?=?3?=?0b’0000?0000?0000?0011 ~x?=?0b’1111?1111?1111?1100
一個(gè)數(shù)與(~x)相與的結(jié)果是sizeof(int)的倍數(shù),即_INTSIZEOF(n)將n圓整為sizeof(int)的倍數(shù)。
② va_start宏根據(jù)(va_list)&v得到第一個(gè)可變參數(shù)前的一個(gè)固定參數(shù)在堆棧中的內(nèi)存地址,加上_INTSIZEOF(v)即v所占內(nèi)存大小后,使ap指向固定參數(shù)后下個(gè)參數(shù)(第一個(gè)可變參數(shù)地址)。
固定參數(shù)的地址用于va_start宏,因此不能聲明為寄存器變量(地址無效)或作為數(shù)組類型(長(zhǎng)度難定)。
③va_arg宏取得type類型的可變參數(shù)值。首先ap+=_INTSIZEOF(type),即ap跳過當(dāng)前可變參數(shù)而指向下個(gè)變參的地址;然后ap-_INTSIZEOF(type)得到當(dāng)前變參的內(nèi)存地址,類型轉(zhuǎn)換后返回當(dāng)前變參值。
va_arg宏的等效實(shí)現(xiàn)如下:
#define?va_arg(ap,t)????(ap?=?ap?+?_INTSIZEOF(t),?*(t?*)(ap?-?_INTSIZEOF(t)))
④va_end宏使ap指針指向空,防止野指針
代碼下載地址:https://gitee.com/zgdy/0_printf
my_printf.c函數(shù)的實(shí)現(xiàn)在注釋中已經(jīng)很清晰了。
一、在PC端來測(cè)試:
①:pc機(jī)中有操作系統(tǒng)所以測(cè)試時(shí),my_printf.h中使用stdio.h中的putchar函數(shù)
#ifndef?_MY_PRINTF_H #define?_MY_PRINTF_H //#include?"uart.h" #include?"stdio.h" #define??__out_putchar??putchar #define??MAX_NUMBER_BYTES??64 extern?int?my_printf_test(void); int?printf(const?char?*fmt,?...); #endif?/*?_MY_PRINTF_H?*/
② main.c:
#include?"my_printf.h" int?main(int?argc,char?**argv) { ????printf("CZG~n"); ????my_printf_test(); ????return?0; }
結(jié)果:
二、在JZ2440上面來測(cè)試:
關(guān)于為什么要加lib1funcs.S文件說明:
?http://blog.csdn.net/czg13548930186/article/details/78797744
①由于ARM裸機(jī)中沒有操作系統(tǒng)環(huán)境,所以要自己實(shí)現(xiàn)putchar函數(shù)(并在uart.h中聲明):
② 將lib1funcs.S和my_printf.c放在Makefile中一同編譯進(jìn)去:
結(jié)果: