<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 練習23:認識達夫設備 > 原文:[Exercise 23: Meet Duff's Device](http://c.learncodethehardway.org/book/ex23.html) > 譯者:[飛龍](https://github.com/wizardforcel) 這個練習是一個腦筋急轉彎,我會向你介紹最著名的C語言黑魔法之一,叫做“達夫設備”,以“發明者”湯姆·達夫的名字命名。這一強大(或邪惡?)的代碼中,幾乎你學過的任何東西都被包裝在一個小的結構中。弄清它的工作機制也是一個好玩的謎題。 > 注 > C的一部分樂趣來源于這種神奇的黑魔法,但這也是使C難以使用的地方。你最好能夠了解這些技巧,因為他會帶給你關于C語言和你計算機的深入理解。但是,你應該永遠都不要使用它們,并總是追求簡單易讀的代碼。 達夫設備由湯姆·達夫“發現”(或創造),它是一個C編譯器的小技巧,本來不應該能夠正常工作。我并不想告訴你做了什么,因為這是一個謎題,等著你來思考并嘗試解決。你需要運行這段代碼,之后嘗試弄清它做了什么,以及為什么可以這樣做。 ```c #include <stdio.h> #include <string.h> #include "dbg.h" int normal_copy(char *from, char *to, int count) { int i = 0; for(i = 0; i < count; i++) { to[i] = from[i]; } return i; } int duffs_device(char *from, char *to, int count) { { int n = (count + 7) / 8; switch(count % 8) { case 0: do { *to++ = *from++; case 7: *to++ = *from++; case 6: *to++ = *from++; case 5: *to++ = *from++; case 4: *to++ = *from++; case 3: *to++ = *from++; case 2: *to++ = *from++; case 1: *to++ = *from++; } while(--n > 0); } } return count; } int zeds_device(char *from, char *to, int count) { { int n = (count + 7) / 8; switch(count % 8) { case 0: again: *to++ = *from++; case 7: *to++ = *from++; case 6: *to++ = *from++; case 5: *to++ = *from++; case 4: *to++ = *from++; case 3: *to++ = *from++; case 2: *to++ = *from++; case 1: *to++ = *from++; if(--n > 0) goto again; } } return count; } int valid_copy(char *data, int count, char expects) { int i = 0; for(i = 0; i < count; i++) { if(data[i] != expects) { log_err("[%d] %c != %c", i, data[i], expects); return 0; } } return 1; } int main(int argc, char *argv[]) { char from[1000] = {'a'}; char to[1000] = {'c'}; int rc = 0; // setup the from to have some stuff memset(from, 'x', 1000); // set it to a failure mode memset(to, 'y', 1000); check(valid_copy(to, 1000, 'y'), "Not initialized right."); // use normal copy to rc = normal_copy(from, to, 1000); check(rc == 1000, "Normal copy failed: %d", rc); check(valid_copy(to, 1000, 'x'), "Normal copy failed."); // reset memset(to, 'y', 1000); // duffs version rc = duffs_device(from, to, 1000); check(rc == 1000, "Duff's device failed: %d", rc); check(valid_copy(to, 1000, 'x'), "Duff's device failed copy."); // reset memset(to, 'y', 1000); // my version rc = zeds_device(from, to, 1000); check(rc == 1000, "Zed's device failed: %d", rc); check(valid_copy(to, 1000, 'x'), "Zed's device failed copy."); return 0; error: return 1; } ``` 這段代碼中我編寫了三個版本的復制函數: `normal_copy` 使用普通的`for`循環來將字符從一個數組復制到另一個。 `duffs_device` 這個就是稱為“達夫設備”的腦筋急轉彎,以湯姆·達夫的名字命名。這段有趣的邪惡代碼應歸咎于他。 `zeds_device` “達夫設備”的另一個版本,其中使用了`goto`來讓你發現一些線索,關于`duffs_device`中奇怪的`do-while`做了什么。 在往下學習之前仔細了解這三個函數,并試著自己解釋代碼都做了什么。 ## 你會看到什么 這個程序沒有任何輸出,它只會執行并退出。你應當在`Valgrind`下運行它并確保沒有任何錯誤。 ## 解決謎題 首先需要了解的一件事,就是C對于它的一些語法是弱檢查的。這就是你可以將`do-while`的一部分放入`switch`語句的一部分的原因,并且在其它地方的另一部分還可以正常工作。如果你觀察帶有`goto again`的我的版本,它實際上更清晰地解釋了工作原理,但要確保你理解了這一部分是如何工作的。 第二件事是`switch`語句的默認貫穿機制可以讓你跳到指定的`case`,并且繼續運行知道`switch`結束。 最后的線索是`count % 8`以及頂端對`n`的計算。 現在,要理解這些函數的工作原理,需要完成下列事情: + 將代碼抄寫在一張紙上。 + 當每個變量在`switch`之前初始化時,在紙的空白區域,把每個變量列在表中。 + 按照`switch`的邏輯模擬執行代碼,之后再正確的`case`處跳出。 + 更新變量表,包括`to`、`from`和它們所指向的數組。 + 當你到達`while`或者我的`goto`時,檢查你的變量,之后按照邏輯返回`do-while`頂端,或者`again`標簽所在的地方。 + 繼續這一手動的執行過程,更新變量,直到確定明白了代碼如何運作。 ## 為什么寫成這樣? 當你弄明白它的實際工作原理時,最終的問題是:問什么要把代碼寫成這樣?這個小技巧的目的是手動編寫“循環展開”。大而長的循環會非常慢,所以提升速度的一個方法就是找到循環中某個固定的部分,之后在循環中復制代碼,序列化地展開。例如,如果你知道一個循環會執行至少20次,你就可以將這20次的內容直接寫在源代碼中。 達夫設備通過將循環展開為8個迭代塊,來完成這件事情。這是個聰明的辦法,并且可以正常工作。但是目前一個好的編譯器也會為你完成這些。你不應該這樣做,除非少數情況下你證明了它的確可以提升速度。 ## 附加題 + 不要再這樣寫代碼了。 + 查詢維基百科的“達夫設備”詞條,并且看看你能不能找到錯誤。將它與這里的版本對比,并且閱讀文章來試著理解,為什么維基百科上的代碼在你這里不能正常工作,但是對于湯姆·達夫可以。 + 創建一些宏,來自動完成任意長度的這種設備。例如,你想創建32個`case`語句,并且不想手動把它們都寫出來時,你會怎么辦?你可以編寫一次展開8個的宏嗎? + 修改`main`函數,執行一些速度檢測,來看看哪個實際上更快。 + 查詢`memcpy`、`memmove`和`memset`,并且也比較一下它們的速度。 + 不要再這樣寫代碼了!
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看