日韩在线不卡免费视频一区,日韩欧美精品一区二区三区经典,日产精品码2码三码四码区,人妻无码一区二区三区免费,日本feerbbwdh少妇丰满

魚鷹單片機(jī)
認(rèn)證:優(yōu)質(zhì)創(chuàng)作者
作者動(dòng)態(tài)
幾款 USB 抓包工具對(duì)比,哪個(gè)是你心中第一?
03-21 13:59
如何將網(wǎng)頁數(shù)據(jù)轉(zhuǎn)化成C語言數(shù)組?
2024-12-05 09:28
關(guān)于RS485自動(dòng)收發(fā)那些天坑
2024-11-15 16:02
為什么大廠都在用 Yocto?
2024-10-24 13:31
什么,VSCode 竟然是代碼編輯器?
2024-10-08 09:13

困惑多年,為什么 printf 可以重定向?

很多人在用 printf 函數(shù)進(jìn)行串口打印的時(shí)候,都會(huì)被告知需要重定向 fputc 函數(shù)(別的平臺(tái)可能不是這個(gè)函數(shù)),讓字符串?dāng)?shù)據(jù)輸出到指定串口,按照網(wǎng)上的教程也能很快解決。但是卻沒人告訴你為什么可以被重定向,為什么明明使用的是 printf 函數(shù),重定向的卻是 fputc 函數(shù)?

使用 51 的時(shí)候,我們也可以使用 printf 函數(shù),但是我們并沒有進(jìn)行重定向,也能使用,這又是為什么?

對(duì)于經(jīng)驗(yàn)豐富的人來說,這些問題心里應(yīng)該都有答案,但考慮到有一些道友可能并不了解,所以今天就稍微水那么一篇吧。

這些問題從大一到大三,一直困惑著魚鷹,直到魚鷹在代碼中看到這么個(gè)東西:

__attribute__((weak))  //  注意兩個(gè)括號(hào)

魚鷹一看,沒見過啊,不懂啊,所以魚鷹趕緊去網(wǎng)上查了一下,不查不要緊,一查嚇一跳,發(fā)現(xiàn) __attribute__ 這個(gè)東東了不得啊,很多C語言屬性都能修改,功能實(shí)在是太強(qiáng)大了,強(qiáng)大到魚鷹自認(rèn)為掌握得不錯(cuò)的C語言都還只是基礎(chǔ),也就只配在小白面前嘚瑟一下。

言歸正傳,為了突出重點(diǎn),今天只講 weak 屬性,以防分心。

我們都知道,函數(shù)名不可以重名,當(dāng)然不同文件內(nèi)聲明的 static 函數(shù)倒是可以解除該限制(可查看《C語言之static》)。

但是如果沒有使用 static ,那么編譯器就會(huì)給你報(bào)錯(cuò),告訴你函數(shù)名重復(fù)咯。

編譯器一共告訴你兩個(gè)信息:

1、重復(fù)的函數(shù)名是 func_name

2、重復(fù)的地方在 board.c 和 main.c 文件里面(后面 .o 表示目標(biāo)文件,由對(duì)應(yīng)的 .c 文件生成)

編譯器一發(fā)出這樣的信息,程序員很快就能找到問題并解決,所以看懂編譯信息很重要(如果有些編譯信息不常見,復(fù)制這條信息到網(wǎng)上一搜,一大堆文章就冒出來了)。

根據(jù)錯(cuò)誤信息,只要修改一處變量名,即可消除該錯(cuò)誤。

那么這個(gè)和printf重定向有什么關(guān)系?

我們知道,printf 最終會(huì)調(diào)用 fputc 進(jìn)行字符串輸出,但是這些函數(shù)是標(biāo)準(zhǔn)庫提供的,而標(biāo)準(zhǔn)庫沒有提供源碼給你,當(dāng)你需要用的時(shí)候添加 <stdio.h> 即可。

但是很多時(shí)候,fputc 輸出的位置可能需要改變,比如輸出到 LCD、串口1、串口2,我們總不可能去修改標(biāo)準(zhǔn)庫的源碼吧,但也沒有源碼提供啊,怎么才能在不修改源碼的情況下滿足這個(gè)需求呢?

方法是有的,比如你可以通過某個(gè)函數(shù)向printf中注冊(cè)一個(gè)回調(diào)函數(shù),讓printf調(diào)用這個(gè)回調(diào)函數(shù)進(jìn)行字符串輸出即可,但是標(biāo)準(zhǔn)庫并沒有提供這個(gè)東西,因?yàn)樗昧烁玫姆绞浇鉀Q這個(gè)問題。

那就是本文的主角,符號(hào)屬性弱化,weak。

假設(shè)我們拿到了標(biāo)準(zhǔn)庫的源碼,能清楚的看到實(shí)現(xiàn)原理,那么我們應(yīng)該能看到一個(gè)fputc的函數(shù)。然后你通過分析原理,發(fā)現(xiàn)它的輸出位置不是自己想要的,那么你會(huì)怎么做?

既然有源碼,好辦,直接修改fputc的實(shí)現(xiàn)即可,簡單。

但現(xiàn)在沒有源碼,怎么辦?但你發(fā)現(xiàn)你在自己的文件里面直接實(shí)現(xiàn)fputc函數(shù)好像也沒事,為什么?就是因?yàn)闃?biāo)準(zhǔn)庫將fputc函數(shù)的屬性進(jìn)行了弱化,即:

這樣做有什么好處?

1、別人可以不需要給你源碼

2、即使沒有源碼,也能間接的達(dá)到修改源碼的目的

3、即使有源碼,通過該屬性設(shè)置,也不需要?jiǎng)h除別人的代碼去重新實(shí)現(xiàn),可以保留原來的代碼。

4、不需要使用回調(diào)函數(shù)的方式進(jìn)行注冊(cè),可以直接重新實(shí)現(xiàn)該函數(shù),非常簡單

5、存在一個(gè)默認(rèn)函數(shù)實(shí)現(xiàn),如果說你不想重新實(shí)現(xiàn)函數(shù),那么編譯器就會(huì)使用該函數(shù)進(jìn)行編譯、鏈接,而不會(huì)在編譯時(shí)出現(xiàn)錯(cuò)誤或警告(這就是為什么即使你沒有重新寫一個(gè)fputc,編譯也不會(huì)報(bào)錯(cuò)的原因)

當(dāng)然,第五點(diǎn),既是好處也是壞處,因?yàn)榫幾g器沒有提示,你就不知道你到底有沒有重新實(shí)現(xiàn)函數(shù)了。

其實(shí)要查看編譯器鏈接的到底是哪一個(gè)函數(shù)很簡單,打開map文件(關(guān)注了這么久,怎么打開的就不多說了),搜索對(duì)應(yīng)的函數(shù)名即可:

你會(huì)發(fā)現(xiàn),雖然 main.c 文件中雖然也有一個(gè) func_name 函數(shù),但實(shí)際上,編譯器鏈接的是 board.c 文件的,原因就是因?yàn)?main.c 文件的 func_name 函數(shù)屬性被弱化了。

這樣一來,即使函數(shù) func_name 在 main() 函數(shù)中被調(diào)用,但它沒有使用本文件的func_name,而是調(diào)用了 board.c 文件中的函數(shù)。

但你將 board.c 文件中的函數(shù)刪掉后,編譯后并不會(huì)出現(xiàn)錯(cuò)誤,并且會(huì)發(fā)現(xiàn) main() 調(diào)用的函數(shù)變成了 main.c 文件中的函數(shù)。

就是這么奇妙!

事實(shí)上,這個(gè)屬性弱化不僅僅在 printf 函數(shù)中體現(xiàn)了,在中斷處理函數(shù)中也做了這樣的處理,只不過這是匯編方式:

這就是為什么你可以在任何文件內(nèi)寫中斷處理函數(shù),而即使你沒有寫中斷處理函數(shù),編譯器也不會(huì)報(bào)錯(cuò)的原因!

當(dāng)然了,這種屬性設(shè)置雖然可以保證不同文件的函數(shù)名可以相同(同一個(gè)文件的函數(shù)名還是不可以相同),但是最終只有一個(gè)函數(shù)會(huì)被編譯器所鏈接!

哦,對(duì)了,如果你要實(shí)現(xiàn)多個(gè)不同的串口打印輸出,不如使用 vsprintf(建議 vsnprintf),好處就是這個(gè)函數(shù)的輸出位置不是fputc,而是你給定的緩存空間,這樣你就可以實(shí)現(xiàn)自己的printf函數(shù)了。

聲明:本內(nèi)容為作者獨(dú)立觀點(diǎn),不代表電子星球立場。未經(jīng)允許不得轉(zhuǎn)載。授權(quán)事宜與稿件投訴,請(qǐng)聯(lián)系:editor@netbroad.com
覺得內(nèi)容不錯(cuò)的朋友,別忘了一鍵三連哦!
贊 1
收藏 2
關(guān)注 160
成為作者 賺取收益
全部留言
0/200
成為第一個(gè)和作者交流的人吧