我做嵌入式行業,編程也多和硬件打交道,好多人說編譯器只是工具,重要的在于算法和思想。這話說的本來沒錯,但要有一個條件在先:那就是你真正掌握了你所用的編譯器。但就我來看,真正熟悉編譯器的卻并不多見。當你深入了解一個編譯器后,你能像用匯編一樣用C,可以像匯編那樣隨心所欲的操作MCU!
了解一個編譯器,首先應該有匯編的基礎,不要求能用匯編編寫程序或做過項目,但至少看的懂!不熟悉匯編的嵌入式程序員是不合格的程序員!
了解一個編譯器,最好的方法是看它自帶的幫助文件,至少要看過Compiler User's Guide ,至少遇到問題會想到到幫助中查找方法,雖然幫助大多是E文。
工作以來一直使用keil MDK編譯器,對于這個編譯器的界面以及設置,可以參考博文:http://blog.csdn.net/zhzht19861011/article/details/5964827,在這里先來看一看keil MDK編譯器的一些細節,看看這些細節,你知道多少。
1. 在所有的內部和外部標識符中,大寫和小寫字符不同。
2. 默認情況下,char 類型的數據項是無符號的。它們可以顯式地聲明為signed char 或 unsigned char。
3.基本數據類型的大小和對齊:
類型 位大小 按字節自然對齊
char 8 1
short 16 2
int 32 4
long 32 4
long long 64 8
float 32 4
double 64 8
long double 64 8
所有指針 32 4
bool (僅用于C++ ) 8 1
_Bool (僅用于C ) 8 1
wchar_t (僅用于C++ ) 16 2
注:a. 通常局部變量保留在寄存器中,但當局部變量太多放到棧里的時候,它們總是字對齊的。例如局部char變量在棧里以4為邊界對齊;
b. 壓縮類型的自然對齊方式為1。使用關鍵字__packed來壓縮特定結構,將所有有效類型的對齊邊界設置為1.
4. 整數以二進制補碼形式表示;浮點量按IEEE格式存儲。
5. 有符號量的右移是算術移位,即移位時要保證符號位不改變。
6. 對于int類的值:超過31位的左移結果為零;無符號值或正的有符號值超過31位的右移結果為零。負的有符號值移位結果為-1。
7. 整數除法的余數的符號于被除數相同,由ISO C90標準得出;
8. 如果整型值被截斷為短的有符號整型,則通過放棄適當數目的最高有效位來得到結果。如果原始數是太大的正或負數,對于新的類型 ,無法保證結果的符號將于原始數相同。所以強制類型轉化的時候,對轉換的結果一定要清晰。
9. 整型數超界不引發異常;像unsigned char test; test=1000;這類是不會報錯的,賦值或計算時務必小心。
10. 默認情況下,整型數除以零返回零。
11. 對于兩個指向相同類型和對齊屬性的指針相減,計算結果如下表達式所示:
((int)a ? (int)b) / (int)sizeof(指向數據的類型)
12. 在嚴格C中,枚舉值必須被表示為整型,例如,必須在?2147483648 到+2147483647的范圍內。但keil MDK自動使用對象包含enum范圍的最小整型來實現(比如char類型),除非使用編譯器命令??enum_is_int 來強制將enum的基礎類型設為至少和整型一樣寬。超出范圍的枚舉值默認僅產生警告:#66: enumeration value is out of "int" range
13. 結構體:struct {
char c;
short s;
int x;
} //這個結構體占8個字節
但是,結構體:
struct {
char c;
int x;
short s;
} //這個結構體占12個字節
這是為什么?
對于結構體填充,據定義結構的方式,keil MDK編譯器用以下方式的一種來填充結構:
定義為static或者extern的結構用零填充;
棧或堆上的結構,例如,用 malloc() 或者 auto定義的結構,使用先前存儲在那些存儲器位置的任何內容進行填充。不能使用memcmp() 來比較以這種方式定義的填充結構!
14. 編譯器不對聲明為volatile 類型的數據進行優化。 我發現還有不少剛入門的嵌入式程序員從沒見過這個關鍵字.
15. __nop():延時一個指令周期,編譯器絕不會優化它。如果硬件支持NOP指令,則該句被替換為NOP指令,如果硬件不支持NOP指令,編譯器將它替換為一個等效于NOP的指令,具體指令由編譯器自己決定。
16. 還有一些編譯器知識,我放在了另外一篇博文里,《有趣的keil mdk細節》。