嵌入式C語言知識點總結(jié)
【硬件電路設計零基礎入門,4節(jié)精品課等著你?。?!】
做個awesomeIT大咖
趁這個假期華麗變身,做夏日最靚的仔!
趕緊掃描下方二維碼,參與信盈達【硬件電路設計訓練營】
打卡一次,即可抱走專屬于你的神秘大禮哦!
奔走相告,信盈達學習福利卷起來了?


怎么做好嵌入式?相信這個問題無論問誰你都會得到一句學好C語言!今天推薦一篇大佬寫的嵌入式C語言知識點總結(jié),非常值得一讀。
從語法上來說C語言并不復雜, 但編寫優(yōu)質(zhì)可靠的嵌入式C程序并非易事,不僅需要熟知硬件特性和缺陷,還需要對編譯原理和計算機技術知識有著一定的了解。
本文以嵌入式實踐為基礎,再結(jié)合相關資料, 闡述嵌入式需要了解的C語言知識和重點,希望每個讀到這篇文章的人都能有所收獲。
1 關鍵字
關鍵字是C語言中具有特殊功能的保留標示符,按照功能可分為
1.數(shù)據(jù)類型(常用char, short, int, long, unsigned, float, double)
2.運算和表達式(=, +, -, *, while, do-while, if, goto, switch-case)
3.數(shù)據(jù)存儲(auto,static ,extern,const,register,volatile,restricted)
4.結(jié)構(gòu)(struct, enum, union,typedef),
5.位操作和邏輯運算(<<, >>, &, |, ~,^, &&),
6.預處理(#define, #include, #error,#if…#elif…#else…#endif等),
7.平臺擴展關鍵字(__asm, __inline,__syscall)
這些關鍵字共同構(gòu)成了嵌入式平臺的C語法。
嵌入式的應用從邏輯上可以抽象為三個部分:
1). 數(shù)據(jù)的輸入(如傳感器,信號,接口輸入),
2). 數(shù)據(jù)的處理(如協(xié)議的解碼和封包,AD采樣值的轉(zhuǎn)換等)
3). 數(shù)據(jù)的輸出(GUI的顯示,輸出的引腳狀態(tài),DA的輸出控制電壓,PWM波的占空比等),
對于數(shù)據(jù)的管理就貫穿著整個嵌入式應用的開發(fā),它包含數(shù)據(jù)類型,存儲空間管理,位和邏輯操作,以及數(shù)據(jù)結(jié)構(gòu),C語言從語法上支撐上述功能的實現(xiàn),并提供相應的優(yōu)化機制,以應對嵌入式下更受限的資源環(huán)境。
2 數(shù)據(jù)類型
C語言支持常用的字符型,整型,浮點型變量,有些編譯器如keil還擴展支持bit(位)和sfr(寄存器)等數(shù)據(jù)類型來滿足特殊的地址操作。
C語言只規(guī)定了每種基本數(shù)據(jù)類型的最小取值范圍,因此在不同芯片平臺上相同類型可能占用不同長度的存儲空間,這就需要在代碼實現(xiàn)時考慮后續(xù)移植的兼容性。
而C語言提供的typedef就是用于處理這種情況的關鍵字,在大部分支持跨平臺的軟件項目中被采用,典型的如下:

既然不同平臺的基本數(shù)據(jù)寬度不同,那么如何確定當前平臺的基礎數(shù)據(jù)類型如int的寬度,這就需要C語言提供的接口sizeof,實現(xiàn)如下:
1 | printf("int size:%d, short size:%d, char size:%d\n", sizeof(int), sizeof(char), sizeof(short));
這里還有重要的知識點,就是指針的寬度,如:

其實這就和芯片的可尋址寬度有關,如32位MCU的寬度就是4,64位MCU的寬度就是8,在有些時候這也是查看MCU位寬比較簡單的方式。
3 內(nèi)存管理和存儲架構(gòu)
C語言允許程序變量在定義時就確定內(nèi)存地址,通過作用域,以及關鍵字extern,static,實現(xiàn)了精細的處理機制,按照在硬件的區(qū)域不同,內(nèi)存分配有三種方式(節(jié)選自C++高質(zhì)量編程):
1). 從靜態(tài)存儲區(qū)域分配。內(nèi)存在程序編譯的時候就已經(jīng)分配好,這塊內(nèi)存在程序的整個運行期間都存在。例如全局變量,static 變量。
2). 在棧上創(chuàng)建。在執(zhí)行函數(shù)時,函數(shù)內(nèi)局部變量的存儲單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時這些存儲單元自動被釋放。棧內(nèi)存分配運算內(nèi)置于處理器的指令集中 ,效率很高,但是分配的內(nèi)存容量有限。
3). 從堆上分配,亦稱動態(tài)內(nèi)存分配。程序在運行的時候用 malloc 或 new 申請任意多少的內(nèi)存,程序員自己負責在何時用 free 或 delete 釋放內(nèi)存。動態(tài)內(nèi)存的生存期由程序員決定,使用非常靈活,但同時遇到問題也最多。
這里先看個簡單的C語言實例。

C語言的作用域不僅描述了標識符的可訪問的區(qū)域,其實也規(guī)定了變量的存儲區(qū)域,在文件作用域的變量st_val和ex_val被分配到靜態(tài)存儲區(qū),其中static關鍵字主要限定變量能否被其它文件訪問。
而代碼塊作用域中的變量a, ptr和local_st_val則要根據(jù)類型的不同,分配到不同的區(qū)域,其中a是局部變量,被分配到棧中;ptr作為指針,由malloc分配空間,因此定義在堆中;而local_st_val則被關鍵字限定,表示分配到靜態(tài)存儲區(qū)。
這里就涉及到重要知識點,static在文件作用域和代碼塊作用域的意義是不同的:在文件作用域用于限定函數(shù)和變量的外部鏈接性(能否被其它文件訪問), 在代碼塊作用域則用于將變量分配到靜態(tài)存儲區(qū)。
對于C語言,如果理解上述知識對于內(nèi)存管理基本就足夠,但對于嵌入式C來說,定義一個變量,它不一定在內(nèi)存(SRAM)中,也有可能在FLASH空間,或直接由寄存器存儲(register定義變量或者高優(yōu)化等級下的部分局部變量),如定義為const的全局變量定義在FLASH中,定義為register的局部變量會被優(yōu)化到直接放在通用寄存器中,在優(yōu)化運行速度,或者存儲受限時,理解這部分知識對于代碼的維護就很有意義。
此外,嵌入式C語言的編譯器中會擴展內(nèi)存管理機制,如支持分散加載機制和attribute((section(“用戶定義區(qū)域”))),允許指定變量存儲在特殊的區(qū)域如(SDRAM, SQI FLASH), 這強化了對內(nèi)存的管理,以適應復雜的應用環(huán)境場景和需求。

采用 這種方式,我們就可以將變量指定到需要的區(qū)域,這在某些情況下必須的,如做GUI或者網(wǎng)頁時因為要存儲大量圖片和文檔,內(nèi)部FLASH空間可能不足,這時就可以將變量聲明到外部區(qū)域,另外內(nèi)存中某些部分的數(shù)據(jù)比較重要,為了避免被其它內(nèi)容覆蓋,可能需要單獨劃分SRAM區(qū)域,避免被誤修改導致致命性的錯誤。
這些經(jīng)驗在實際的產(chǎn)品開發(fā)中是常用且重要,不過因為篇幅原因,這里只簡略的提供例子,如果工作中遇到這種需求,建議詳細去了解下。
至于堆的使用,對于嵌入式Linux來說,使用起來和標準C語言一致,注意malloc后的檢查,釋放后記得置空,避免"野指針“,不過對于資源受限的單片機來說,使用malloc的場景一般較少,如果需要頻繁申請內(nèi)存塊的場景,都會構(gòu)建基于靜態(tài)存儲區(qū)和內(nèi)存塊分割的一套內(nèi)存管理機制,一方面效率會更高(用固定大小的塊提前分割,在使用時直接查找編號處理),另一方面對于內(nèi)存塊的使用可控,可以有效避免內(nèi)存碎片的問題,常見的如RTOS和網(wǎng)絡LWIP都是采用這種機制,我個人習慣也采用這種方式,所以關于堆的細節(jié)不在描述,如果希望了解,可以參考中關于存儲相關的說明。

版權(quán)聲明:本文為CSDN博主「huaijin622」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。如有侵權(quán),請聯(lián)系刪除。
原文鏈接:https://blog.csdn.net/huaijin520/article/details/108298227