在有作業系統機器上,我們很少關心輸入和輸出的問題。因為有很多現成的庫函式供我們呼叫。在做裸板開發時,可沒有現成庫函式供我們呼叫,一切都需要我們自己實現。
下面我們通過串列埠在裸板上實現乙個printf和scanf函式。
printf主要用來進行格式化輸出,scanf函式主要用來進行格式化輸入的。這裡個函式都是不定引數函式,這裡簡單介紹一下不定參函式實現方法。
一、不定引數的造型
function(type arg,...);
一般第乙個引數arg指定引數的個數,
int function(3,arg1,arg2,arg3);
這是一種顯示的告訴編譯器有幾個引數,第乙個引數3,即代表後面有三個引數。
但也不是唯一的,比如printf的第乙個引數是乙個字串,它是通過這個引數來確定有幾個引數的.
int printf(const char *format, ...);
例如我們呼叫的時候
printf("%d,%s,%d",i,str,j);
第乙個引數是"%d,%s,%d",通過分析它我們可以知道有幾個
二、函式的引數壓棧
1. 固定的引數函式呼叫過程
一般函式引數入棧是按照從右向左的順序入棧。這樣第乙個引數arg1就放在了棧頂的位置。
2.變參函式呼叫過程
通過上面的引數入棧方式我們可以得到如下結論:
如果想將棧中的引數讀出來,我們只需要知道,棧頂元素的位址即第乙個引數的位址即可。通過前面變參函式的分析,通過變參函式第乙個引數可以知道傳遞的引數個數。根據引數入棧的順序,我們可以知道第乙個引數是放在棧頂位置的。
總結一下,現在我們已經獲得兩個條件了:
1.棧中引數的個數
2.棧頂元素的位址
有了這兩個條件,我們就可以從棧中讀取我們想要的引數了。
當然,每個引數都有自己的型別,還有的就是位元組對齊了。在讀取引數的時候,這些問題都必須考慮到。
幸運的是這些問題在已經被大牛們解決了,已經封裝成相應的巨集,我們在操作的時候只需要知道這些巨集的含義即可。
三、變參函式常用巨集
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_end(ap) (ap = (va_list)0)
這些巨集在不同的作業系統,有不同的實現,想使用的話,只需要包含標頭檔案stdarg.h就可以了。
(1)va_start巨集的作用 :
v是第乙個引數,通過前面我們知道,第乙個引數就是用來表明有幾個引數,它不是我們實際需要的引數。我們通過它來計算出,第乙個實際引數的位址,主意哦是實際引數,可不是第乙個表明引數個數的引數位址,讓ap指標變數儲存。
(1)va_arg巨集的作用:
通過va_start,我們的ap的指標已經指向了第乙個實際引數。
可以看到的是ap指標先更新了,然後又減了乙個值,最終把這個值返回。這裡面的t代表即將獲得引數的型別。
可以看出,通過va_arg巨集我們獲得每個實際引數的值。
(2)va_end巨集的作用
將ap指標賦值為null,即0
下面我們自己寫乙個測試程式來看一下,這些巨集怎麼使用。
#include
<
stdio.h
>
#include
<
stdlib.h
>
#include
<
stdarg.h
>
intmy_printf
(char
*fmt,.
..)}
else
}//將ap賦值為null
va_end(ap
);return 0;}
intmain
(int
argc
,const char
*argv
) 實際上,格式化的轉換有現成的函式可以呼叫,例如:vsprintf()和vsscanf()這些函式的源**可以從bootloader和核心原始碼上獲得。
四、常用格式轉換函式
int vsprintf(char *str, const char *format, va_list ap);
這個函式的功能,就是把輸入的格式字串進行解釋,把解釋好的字串放在str。這個函式的原始碼可以直接在核心中獲得。
int vsscanf(const char *str, const char *format, va_list ap);
str中是我們從鍵盤上輸入的一些字串,format是我們呼叫scanf的時候輸入的格式串。通過這些資訊,vsscanf函式解發布每個變數應該賦為什麼值。這個函式的原始碼可以直接在核心中獲得。
有了這兩個函式後,我們就可以通過串列埠封裝自己的printf和scanf了。
(1)通過串列埠實現printf函式
intprintf
(const
char
*fmt,.
..)return n;}
(2)通過串列埠實現scanf函式
intscanf
(const char
*fmt,.
..)else
}va_start
(args
,fmt);
i =vsscanf
(buffer
,fmt
,args);
va_end
(args);
send_char
('r');
send_char
('n');
return i;}
通過串列埠實現printf和scanf函式
草根老師部落格 程姚根 在做裸板開發時,常常需要通過輸出或者通過串列埠輸入一些資訊。在有作業系統機器上,我們很少關心輸入和輸出的問題。因為有很多現成的庫函式供我們呼叫。在做裸板開發時,可沒有現成庫函式供我們呼叫,一切都需要我們自己實現。下面我們通過串列埠在裸板上實現乙個printf和scanf函式。...
使用串列埠實現接收和傳送功能
一 前言 本文以串列埠 usart1為例,初步實現接收和傳送功能,使用的是stm32f1板子,基於火哥教學做的自我小結。電腦裝置需要安裝串列埠除錯助手和usb轉串列埠ch340g的驅動。二 正文 1.硬體設計 將 ch340g 的 txd 引腳與 usart1 的 rx 引腳連線,ch340g 的 ...
串列埠實現FIFO接受資料
基本原理 靜態佇列 串列埠的fifo簡單讀取實現 功能,實現串列埠的fifo實現 使用方法 版本 v1.0.0 include sys.h include usartbuf.h usartype usart fifo read usart recerivepoint rusart,uint8 t b...