1. 程式人生 > >嵌入式領域,你要了解你的編譯器

嵌入式領域,你要了解你的編譯器

      我做嵌入式行業,程式設計也多和硬體打交道,好多人說編譯器只是工具,重要的在於演算法和思想。這話說的本來沒錯,但要有一個條件在先:那就是你真正掌握了你所用的編譯器。但就我來看,真正熟悉編譯器的卻並不多見。當你深入瞭解一個編譯器後,你能像用匯編一樣用C,可以像彙編那樣隨心所欲的操作MCU!

      瞭解一個編譯器,首先應該有彙編的基礎,不要求能用匯編編寫程式或做過專案,但至少看的懂!不熟悉彙編的嵌入式程式設計師是不合格的程式設計師!

      瞭解一個編譯器,最好的方法是看它自帶的幫助檔案,至少要看過Compiler User's Guide ,至少遇到問題會想到到幫助中查詢方法,雖然幫助大多是E文。

         1. 在所有的內部和外部識別符號中,大寫和小寫字元不同。

          2. 預設情況下,char 型別的資料項是無符號的。它們可以顯式地宣告為signed char 或 unsigned char。

          3.基本資料型別的大小和對齊:

型別        位大小       按位元組自然對齊
char81
short162
int324
long324
long long648
float324
double648
long double648
所有指標324
bool (僅用於C++ )81
_Bool (僅用於C )81
wchar_t (僅用於C++ )162

注: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細節》

後記:關於Keil MDK的應該掌握的知識,在《編寫優質嵌入式C程式》一文的第三章中,做了進一步總結,可以直接檢視該文。