一段完美的代碼不僅在於找到一個給定的問題的解決方案,但在它的簡單性,有效性,緊湊性和效率(內存)。設計的代碼比實際執行更難。因此,每一個程序員當用C語言開發時,都應該保持這些基本的東西在頭腦中。本文向你介紹規范你的C代碼的幾種方法。 1、在可能的情況下使用typedef替代macro.當然有時候你無法避免macro,但是typedef更好。 typedef int* INT_PTR; INT_PTR a ,b; # define INT_PTR int*; INT_PTR a ,b; 在這個宏定義中,a是一個指向整數的指針,而b是只有一個整數聲明。使用typedef a和b都是整數的指針。 2、在一個邏輯條件語句中常數項永遠在左側。 int x = 4; if (x = 1) { x = x + 2; printf("%d",x);// Output is 3 } int x = 4; if (1 = x) { x = x + 2; printf("%d",x); // Compilation error } 使用"="賦值運算符,替代"=="相等運算符,這是個常見的輸入錯誤。常數項放在左側,將產生一個編譯時錯誤,讓你輕松捕獲你的錯誤。注:"="是賦值運算符。b = 1會設置變量b等於值1。"=="相等運算符。如果左側等於右側,返回true,否則返回false。 3、確保聲明和定義是靜態的,除非您希望從不同的文件中調用該函數。 在同一文件函數對其他函數可見,才稱之為靜態函數。它限制其他訪問內部函數,如果我們希望從外界隱藏該函數。現在我們並不需要為內部函數創建頭文件,其他看不到該函數。靜態聲明一個函數的優點包括: (1)兩個或兩個以上具有相同名稱的靜態函數,可用於在不同的文件。 (2)編譯消耗減少,因為沒有外部符號處理。 4、節約內存(內存對齊和填充的概念) struct { char c; int i; short s;}str_1; struct { char c; shorts; inti;}str_2; 假設一個字符需要1個字節,short占用2個字節和int需要4字節的內存。起初,我們會認為上面定義的結構是相同的,因此占據相同數量的內存。然而,而str_1占用12個字節,第二個結構只需要8個字節?這怎么可能呢? 請注意,在第一個結構,3個不同的4個字節被分配到三種數據類型,而在第二個結構的前4個自己char和short可以被采用,int可以采納在第二個的4個字節邊界(一共8個字節) 5、使用無符號整數,而不是整數的,如果你知道的值將永遠是否定的。 有些處理器可以處理無符號的整數,比有符號整數的運算速度要快。(這也是很好的實踐,幫助self-documenting代碼) 6、switch-case語句。在程序中經常會使用switch-case語句,每一個由機器語言實現的測試和跳轉僅僅是為了決定下一步要做什么,就浪費了處理器時間。為了提高速度,可以把具體的情況按照它們發生的相對頻率排序。即把最可能發生的情況放在第一,最不可能發生的情況放在最后,這樣會減少平均的代碼執行時間。 當switch語句中的case標號很多時,為了減少比較的次數,明智的做法是把大switch語句轉為嵌套switch語句。把發生頻率高的case標號放在一個switch語句中,並且是嵌套switch語句的最外層,發生相對頻率相對低的case標號放在另一個switch語句中。比如,下面的程序段把相對發生頻率低的情況放在缺省的case標號內。 pMsg=ReceiveMessage(); switch (pMsg->type) { case FREQUENT_MSG1: handleFrequentMsg(); break; case FREQUENT_MSG2: handleFrequentMsg2(); break; ...... case FREQUENT_MSGn: handleFrequentMsgn(); break; default: //嵌套部分用來處理不經常發生的消息 switch (pMsg->type) { case INFREQUENT_MSG1: handleInfrequentMsg1(); break; case INFREQUENT_MSG2: handleInfrequentMsg2(); break; ...... case INFREQUENT_MSGm: handleInfrequentMsgm(); break; } } 如果switch中每一種情況下都有很多的工作要做,那么把整個switch語句用一個指向函數指針的表來替換會更加有效,比如下面的switch語句,有三種情況: enum MsgType{Msg1, Msg2, Msg3} switch (ReceiveMessage()) { case Msg1;...... case Msg2;..... case Msg3;..... } 為了提高執行速度,用下面這段代碼來替換這個上面的switch語句 /*准備工作*/ int handleMsg1(void); int handleMsg2(void); int handleMsg3(void); /*創建一個函數指針數組*/ int (*MsgFunction [])()={handleMsg1, handleMsg2, handleMsg3}; /*用下面這行更有效的代碼來替換switch語句*/ status=MsgFunction[ReceiveMessage()](); 7、全局變量。使用全局變量比向函數傳遞參數更加有效率,這樣做去除了函數調用前參數入棧和函數完成后參數出棧的需要。當然,使用全局變量會對程序有一些負作用。使用全局變量比函數傳遞參數更加有效率。這樣做去除了函數調用參數入棧和函數完成后參數出棧所需要的時間。然而決定使用全局變量會影響程序的模塊化和重入,故要慎重使用。 8、嵌入式系統編程應避免使用標准庫例程,因為很多大的庫例程設法處理所有可能的情況,所以占用了龐大的內存空間,因而應盡可能地減少使用標准庫例程。 9、Inline函數。在C++中,關鍵字Inline可以被加入到任何函數的聲明中。這個關鍵字請求編譯器用函數內部的代碼替換所有對於指出的函數的調用。這樣做在兩個方面快於函數調用。這樣做在兩個方面快於函數調用:第一,省去了調用指令需要的執行時間;第二,省去了傳遞變元和傳遞過程需要的時間。但是使用這種方法在優化程序速度的同時,程序長度變大了,因此需要更多的ROM。使用這種優化在Inline函數頻繁調用並且只包含幾行代碼的時候是最有效的。 10、用指針代替數組。在許多種情況下,可以用指針運算代替數組索引,這樣做常常能產生又快又短的代碼。與數組索引相比,指針一般能使代碼速度更快,占用空間更少。使用多維數組時差異更明顯。下面的代碼作用是相同的,但是效率不一樣。 數組索引 指針運算 For(;;){ p=array A=array[t++]; for(;;){ a=*(p++); ...... ...... } 指針方法的優點是,array的地址每次裝入地址p后,在每次循環中只需對p增量操作。在數組索引方法中,每次循環中都必須進行基於t值求數組下標的復雜運算。 11、不定義不使用的返回值。function函數定義並不知道函數返回值是否被使用,假如返回值從來不會被用到,應該使用void來明確聲明函數不返回任何值。 12、手動編寫匯編。在嵌入式軟件開發中,一些軟件模塊最好用匯編語言來寫,這可以使程序更加有效。雖然C/C++編譯器對代碼進行了優化,但是適當的使用內聯匯編指令可以有效的提高整個系統運行的效率。 13、使用寄存器變量。在聲明局部變量的時候可以使用register關鍵字。這就使得編譯器把變量放入一個多用途的寄存器中,而不是在堆棧中,合理使用這種方法可以提高執行速度。函數調用越是頻繁,越是可能提高代碼的速度。 14、使用增量和減量操作符。在使用到加一和減一操作時盡量使用增量和減量操作符,因為增量符語句比賦值語句更快,原因在於對大多數CPU來說,對內存字的增、減量操作不必明顯地使用取內存和寫內存的指令,比如下面這條語句:x=x+1; 模仿大多數微機匯編語言為例,產生的代碼類似於: move A,x ;把x從內存取出存入累加A add A,1 ;累加器A加1 store x ;把新值存回x 如果使用增量操作符,生成的代碼下: incr x ;x加1 顯然,不用取指令和存指令,增、減量操作執行的速度加快,同時長度也縮短了。
