一、變量與值得比較
1、布爾變量與零值的比較
不可將布爾變量直接與 TRUE、 FALSE或者 1、 0進行比較 。據(jù)布爾類型的語義,零值為“ 假”(記為 FALSE),任何非零值都是“ 真”(記為TRUE)。
TRUE的值究竟是什么并沒有統(tǒng)一的標準。例如 Visual C++ 將 TRUE定義為 1,而 Visual Basic則將 TRUE定義為-1 。
假設布爾變量名字為 flag,它與零值比較的標準 if語句如下:
?
if (flag) // 表示flag為真 if (!flag) // 表示flag為假
?
其它的用法都屬于不良風格,例如:
?
if (flag == TRUE) if (flag == 1 ) if (flag == FALSE) if (flag == 0)
?
2、整形變量與零值的比較
應當將整型變量用“ ==” 或“ !=” 直接與 0比較 。假設整型變量的名字為 value,它與零值比較的標準 if語句如下:
?
if (value == 0) if (value != 0)
?
不可模仿布爾變量的風格而寫成:
?
if (value) // 會讓人誤解 value是布爾變量 if (!value)
?
3、浮點變量與零值的比較
不可將浮點變量用“ ==” 或“ !=” 與任何數(shù)字比較 。千萬要留意, 無論是 float還是 double類型的變量, 都有精度限制。
所以一定要避免將浮點變量用“ ==” 或“ !=” 與數(shù)字比較,應該設法轉(zhuǎn)化成“ >=” 或“ <=” 形式。假設浮點變量的名字為 x,應當 將:
?
if (x == 0.0) // 隱含錯誤的比
?
轉(zhuǎn)化為:
?
if ((x>=-EPSINON) && (x<=EPSINON))
?
其中 EPSINON是允許的誤差(即精度) 。
4、指針變量與零值的比較
應當將指針變量用“ ==” 或“ !=” 與 NULL比較 。指針變量的零值是“ 空”(記為 NULL)。
盡管 NULL 的值與 0相同,但是兩者意義不同。假設指針變量的名字為 p,它與零值比較的標準 if語句如下:
?
if (p == NULL) // p與 NULL顯式比較,強調(diào) p是指針變量 if (p != NULL)
?
不要寫成:
?
if (p == 0) // 容易讓人誤解 p是整型變量 if (p != 0)
?
或者:
?
if (p) // 容易讓人誤解p是布爾變量 if (!p)
?
二、變量及基本運算
1、整型數(shù)
如果我們確定整數(shù)非負,就應該使用unsigned int而不是int。
有些處理器處理無符號unsigned 整形數(shù)的效率遠遠高于有符號signed整形數(shù)(這是一種很好的做法,也有利于代碼具體類型的自解釋)。
因此,在一個緊密循環(huán)中,聲明一個int整形變量的最好方法是:
?
register unsigned int variable_name;
?
記住,整形in的運算速度高浮點型float,并且可以被處理器直接完成運算,而不需要借助于FPU(浮點運算單元)或者浮點型運算庫。
盡管這不保證編譯器一定會使用到寄存器存儲變量,也不能保證處理器處理能更高效處理unsigned整型,但這對于所有的編譯器是通用的。
例如在一個計算包中,如果需要結(jié)果精確到小數(shù)點后兩位,我們可以將其乘以100,然后盡可能晚的把它轉(zhuǎn)換為浮點型數(shù)字。
2、除法和取余數(shù)
在標準處理器中,對于分子和分母,一個32位的除法需要使用20至140次循環(huán)操作。
除法函數(shù)消耗的時間包括一個常量時間加上每一位除法消耗的時間。
?
Time (numerator / denominator) = C0 + C1* log2 (numerator / denominator) = C0 + C1 * (log2 (numerator) - log2 (denominator)).
?
對于ARM處理器,這個版本需要20+4.3N次循環(huán)。這是一個消耗很大的操作,應該盡可能的避免執(zhí)行。
有時,可以通過乘法表達式來替代除法。例如,假如我們知道b是正數(shù)并且b*c是個整數(shù),那么(a/b)>c可以改寫為a>(c*b)。
如果確定操作數(shù)是無符號unsigned的,使用無符號unsigned除法更好一些,因為它比有符號signed除法效率高。
3、取模的一種替代方法
我們使用取余數(shù)操作符來提供算數(shù)取模。但有時可以結(jié)合使用if語句進行取模操作。考慮如下兩個例子:
?
uint modulo_func1 (uint count) { return (++count % 60); } uint modulo_func2 (uint count) { if (++count >= 60) count = 0; return (count); }
?
優(yōu)先使用if語句,而不是取余數(shù)運算符,因為if語句的執(zhí)行速度更快。這里注意新版本函數(shù)只有在我們知道輸入的count結(jié)余0至59時在能正確的工作。
4、使用數(shù)組下標
如果你想給一個變量設置一個代表某種意思的字符值,你可能會這樣做:
?
switch ( queue ) { case 0 : letter = 'W'; break; case 1 : letter = 'S'; break; case 2 : letter = 'U'; break; }
?
或者這樣做:
?
if ( queue == 0 ) letter = 'W'; else if ( queue == 1 ) letter = 'S'; else letter = 'U';
?
一種更簡潔、更快的方法是使用數(shù)組下標獲取字符數(shù)組的值。如下:
?
static char *classes="WSU"; letter = classes[queue];
?
5、使用別名
考慮如下的例子:
?
void func1( int *data ) { int i; for(i=0; i<10; i++) { anyfunc( *data, i); } }
?
盡管*data的值可能從未被改變,但編譯器并不知道anyfunc函數(shù)不會修改它,所以程序必須在每次使用它的時候從內(nèi)存中讀取它。如果我們知道變量的值不會被改變,那么就應該使用如下的編碼:
?
void func1( int *data ) { int i; int localdata; localdata = *data; for(i=0; i<10; i++) { anyfunc ( localdata, i); } }
?
這為編譯器優(yōu)化代碼提供了條件。
6、局部變量的類型
我們應該盡可能的不使用char和short類型的局部變量。對于char和short類型,編譯器需要在每次賦值的時候?qū)⒕植孔兞繙p少到8或者16位。
這對于有符號變量稱之為有符號擴展,對于無符號變量稱之為零擴展。這些擴展可以通過寄存器左移24或者16位,然后根據(jù)有無符號標志右移相同的位數(shù)實現(xiàn),這會消耗兩次計算機指令操作(無符號char類型的零擴展僅需要消耗一次計算機指令)。
可以通過使用int和unsigned int類型的局部變量來避免這樣的移位操作。這對于先加載數(shù)據(jù)到局部變量,然后處理局部變量數(shù)據(jù)值這樣的操作非常重要。無論輸入輸出數(shù)據(jù)是8位或者16位,將它們考慮為32位是值得的。
考慮下面的三個函數(shù):
?
int wordinc (int a) { return a + 1; } short shortinc (short a) { return a + 1; } char charinc (char a) { return a + 1; }
?
盡管結(jié)果均相同,但是第一個程序片段運行速度高于后兩者。
三、循環(huán)語句
1、多重循環(huán)
在多重循環(huán)中, 如果有可能, 應當將最長的循環(huán)放在最內(nèi)層, 最短的循環(huán)放在最外層,以減少 CPU 跨切循環(huán)層的次數(shù)。例如示例 4-4(b)的效率比示例4-4(a)的高 :
2、循環(huán)體內(nèi)的判斷
如果循環(huán)體內(nèi)存在邏輯判斷, 并且循環(huán)次數(shù)很大, 宜將邏輯判斷移到循環(huán)體的外面。
示例 4-4(c)的程序比示例 4-4(d)多執(zhí)行了 N-1次邏輯判斷。并且由于前者老要進行邏輯判斷,打斷了循環(huán)“ 流水線” 作業(yè),使得編譯器不能對循環(huán)進行優(yōu)化處理, 降低了效率。
如果N非常大, 最好采用示例 4-4(d)的寫法, 可以提高效率。如果 N非常小,兩者效率差別并不明顯,采用示例 4-4(c)的寫法比較好, 因為程序更加簡潔。
3、for 語句的循環(huán)控制變量
不可在 for 循環(huán)體內(nèi)修改循環(huán)變量,防止 for 循環(huán)失去控制 。建議 for語句的循環(huán)控制變量的取值采用“ 半開半閉區(qū)間” 寫法。
示例 4-5(a)中的 x值屬于半開半閉區(qū)間“ 0 =< x < N”,起點到終點的間隔為 N,循環(huán)次數(shù)為 N。
示例 4-5(b)中的 x值屬于閉區(qū)間“ 0 =< x <= N-1”,起點到終點的間隔為 N-1,循環(huán)次數(shù)為 N。
相比之下,示例 4-5(a)的寫法更加直觀,盡管兩者的功能是相同的 。
4、更快的for()循環(huán)
這是一個簡單而高效的概念。通常,我們編寫for循環(huán)代碼如下:
?
for( i=0; i<10; i++){ ... }
?
i從0循環(huán)到9。如果我們不介意循環(huán)計數(shù)的順序,我們可以這樣寫:
?
for( i=10; i--; ) { ... }
?
這樣快的原因是因為它能更快的處理i的值–測試條件是:i是非零的嗎?如果這樣,遞減i的值。對于上面的代碼,處理器需要計算“計算i減去10,其值非負嗎?
如果非負,i遞增并繼續(xù)”。簡單的循環(huán)卻有很大的不同。這樣,i從9遞減到0,這樣的循環(huán)執(zhí)行速度更快。
這里的語法有點奇怪,但確實合法的。循環(huán)中的第三條語句是可選的(無限循環(huán)可以寫為for(;;))。如下代碼擁有同樣的效果:
?
for(i=10; i; i--){}
?
或者更進一步的:
?
for(i=10; i!=0; i--){}
?
這里我們需要記住的是循環(huán)必須終止于0(因此,如果在50到80之間循環(huán),這不會起作用),并且循環(huán)計數(shù)器是遞減的。使用遞增循環(huán)計數(shù)器的代碼不享有這種優(yōu)化。
四、指針
我們應該盡可能的使用引用值的方式傳遞結(jié)構(gòu)數(shù)據(jù),也就是說使用指針,否則傳遞的數(shù)據(jù)會被拷貝到棧中,從而降低程序的性能。
函數(shù)通過參數(shù)接受結(jié)構(gòu)數(shù)據(jù)的指針,如果我們確定不改變數(shù)據(jù)的值,我們需要將指針指向的內(nèi)容定義為常量。例如:
?
void print_data_of_a_structure ( const Thestruct *data_pointer) { ...printf contents of the structure... }
?
這個示例告訴編譯器函數(shù)不會改變外部參數(shù)的值(使用const修飾),并且不用在每次訪問時都進行讀取。
同時,確保編譯器限制任何對只讀結(jié)構(gòu)的修改操作從而給予結(jié)構(gòu)數(shù)據(jù)額外的保護。
五、懶檢測開發(fā)
在if(a>10 && b=4)這樣的語句中,確保AND表達式的第一部分最可能較快的給出結(jié)果(或者最早、最快計算),這樣第二部分便有可能不需要執(zhí)行。
六、用switch()函數(shù)替代if…else…
對于涉及if…else…else…這樣的多條件判斷,例如:
?
if( val == 1) dostuff1(); else if (val == 2) dostuff2(); else if (val == 3) dostuff3();
?
使用switch可能更快:
?
switch( val ) { case 1: dostuff1(); break; case 2: dostuff2(); break; case 3: dostuff3(); break; }
?
在if()語句中,如果最后一條語句命中,之前的條件都需要被測試執(zhí)行一次。switch允許我們不做額外的測試。如果必須使用if…else…語句,將最可能執(zhí)行的放在最前面。
審核編輯:湯梓紅
評論
查看更多