經典代碼總結(譯)

經典代碼總結(譯)

原文作者:Steve Baker 原文地址:http://www.sjbaker.org/wiki/index.php?title=Cool_Code_list

譯者:多多它爹 譯文地址:http://blog.csdn.net/liuyu790810/archive/2008/04/15/2293880.aspx

下面是我收集的一些精妙的C代碼和一些C++技巧。下面的代碼可能需要我們做些調整才能夠工作,因爲我盡力保持代碼實際執行更快,或者更緊湊。

除非我明確指出,否則所有變量都是無符號的32位整數。

倒轉一個32位word的所有位

我在Linux fortune cookie代碼中看到了如下的代碼 (!)

  n = ((n >>  1& 0x55555555| ((n <<  1& 0xaaaaaaaa) ;
  n 
= ((n >>  2& 0x33333333| ((n <<  2& 0xcccccccc) ;
  n 
= ((n >>  4& 0x0f0f0f0f| ((n <<  4& 0xf0f0f0f0) ;
  n 
= ((n >>  8& 0x00ff00ff| ((n <<  8& 0xff00ff00) ;
  n 
= ((n >> 16& 0x0000ffff| ((n << 16& 0xffff0000) ;

可以根據word的大小定製你自己的轉換函數。

計算32位word中包含'1'的個數

John C. Wren把如下代碼發給我。令人吃驚的是,它很象前一個技巧——位倒轉。

  n = (n & 0x55555555+ ((n & 0xaaaaaaaa>> 1);
  n 
= (n & 0x33333333+ ((n & 0xcccccccc>> 2);
  n 
= (n & 0x0f0f0f0f+ ((n & 0xf0f0f0f0>> 4);
  n 
= (n & 0x00ff00ff+ ((n & 0xff00ff00>> 8);
  n 
= (n & 0x0000ffff+ ((n & 0xffff0000>> 16);

判斷數n是否爲2的冪

 b = ((n&(n-1))==0) ;

(注意:如果數n爲2的冪,上述代碼將b設置爲真。在此語意下,0和1也被認爲是2的冪)

上述代碼通過將數n的最低有效‘1’位的值(least significant '1' bit )置爲‘0’實現。如果數n是2的冪,那麼,它只有一位是‘1’,經過運算後,n&(n-1)爲0,即b爲真。

因此,上述表達式中間的那部分也是一個小技巧……

最低有效‘1’位置0

 n&(n-1)

最低有效n位置爲1 

 ~(~0<<n)

交換兩個整數的值 

這段代碼的精妙就在於,它沒有使用臨時變量,並且相對於普通的方法不容易出錯。這個方法已經流傳了好多年了,所以我並不知道是誰發明了這個方法。

 x = x ^ y ;
 y 
= x ^ y ;
 x 
= x ^ y ;

(操作符‘^’代表C/C++語言中的位運算符——異或,而不是人們所想的ex-FORTRAN語言中的冪運算符)

然而,Scott Smith指出,下面這種更爲優雅的寫法和上面是等價的:

 x ^= y ^= x ^= y ;

因爲同樣的變量在一個語句中被改變兩次,上面的語句是不符合C++語法的。

轉換數字爲ASCII的16進製表示

另一段古老的代碼。我第一次見到此段代碼是在vi的代碼中,但是,它或許比那還要早很多。

   "0123456789ABCDEF" [ n ]

(n的範圍: 0..15)

我也見過如下表示法:

   n [ "0123456789ABCDEF" ]

如果變量n爲字符變量,並且你關閉了足夠多的編譯錯誤檢查開關,上述代碼即可工作。

轉換所有非零值爲1

Pat Down發送這段代碼給我:

   b = !!a ;

如果a爲0,則b爲0,否則b爲1。

小端序還是大端序?

一些計算機存儲整數的最重要字節(MSB)在最前面,而後是次重要字節(LSB),另外一些計算機則採用了相反的方法。前面的那種形式我們稱之爲大端序(big-endian),後面的叫小端序(little-endian)。Intel的計算機均採用了小端序,其餘的採用了大端序。

大多數人們並沒有意識到術語大端序和小端序是來自小說《格利佛遊記》(Gulliver's Travels)。兩個國家Lilliput和 Blefuscu 由於爭論煮熟的雞蛋到底是應該從哪頭打開而發生了一起可怕、血腥的戰爭,這即是大端序和小端序的由來。

CPU製造廠商之間的爭論同樣無聊,而且還有着一些損害。

格利佛說道:“...all true Believers shall break their Eggs at the convenient End: and which is the convenient End, seems, in my humble Opinion, to be left to every Man's Conscience, or at least in the power of the Chief Magistrate to determine。”

總之,採用如下方法來決定你所使用的計算機是大端序還是小端序:

 int i = 1 ;
 little_endian 
= *((char *&i ) ;

達夫設備(Duffs Device)

任何經典代碼集都不能少了達夫設備:

int a = some_number ;
int n = ( a + 4 ) / 5 ;
switch ( a % 5 )
{
  
case 0do
          
{
            putchar ( 
'*' ) ;
  
case 4:   putchar ( '*' ) ;
  
case 3:   putchar ( '*' ) ;
  
case 2:   putchar ( '*' ) ;
  
case 1:   putchar ( '*' ) ;
          }
 while ( --n ) ;
}

printf ( 
" " ) ;

上面循環打印了‘a’個星號,但是它的循環條件是部分解開的(這對於某些應用提高循環速度很重要)。包括編譯器在內的大多數人都會爲此而吃驚。

據說,達夫設備最大的問題就是如何縮進的問題。


如果你有任何好的例子,可以發email給我: [email protected]

譯者注:Duff's Device是一個加速循環語句的C編碼技巧。其基本思想是,如果在一個for循環中,其中操作執行得如果足夠快(比如說,嗯,一個賦值)——那麼測試循環條件佔用了循環所用時間的很大部分。循環應該被部分解開,這樣數個操作一次完成,測試操作也做的較少。比如,如果你填充一個對象區間,你可能要在一次循環中賦二個或更多連續對象的值。你必須注意終止條件的細節及其他。在這裏Duff's Device是個新穎的,有創造力的解決方案。我們來很快地看一個基於Duff's Device的泛型填充算法的實現。函數包含一個switch語句,它的 case語句同時位於一個while循環體內(有一個case語句在外面)。switch內的表達式計算被八除的餘數。執行開始於while循環內的哪個位置由這個餘數決定。最終循環退出。(沒有break)Duff's Device這樣就簡單漂亮地解決了邊界條件的問題。順便提一下,爲什麼“case 0”標記在循環外面呢?這樣不是打破了對稱的美觀嗎?這樣做的唯一理由是爲了處理空序列。當餘數爲零,“case 0”內就需要執行一個多餘的測試來判斷空序列的可能性。總之所有這些都無懈可擊。
結果是判斷語句少執行八倍。這樣,測試本身在循環持續期間所佔的開銷比例下降了八倍這個因數。當然,你也同樣可以嘗試用其他因數。Duff's Device對效率的負面影響可能來自於代碼膨脹(一些處理器更善於處理緊湊的循環而不是大的循環)和特別的結構。優化器被做成當遇到更熟悉簡單的循環代碼時說“啊哈!”,而遇到一些更加技巧性的結構時可能會不知所措從而生成比較保守的代碼。

發佈了15 篇原創文章 · 獲贊 1 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章