當前位置: 妍妍網 > 碼農

MCU死迴圈,國外工程師為什麽喜歡for(;;) ?

2024-07-03碼農

首先,問大家一個問題:你們寫微控制器程式【死迴圈】時,喜歡用用 for(;;) 還是 while(1)?

一位工程師發現,國外工程師在給demo在做死迴圈時用的是for(;;),而不是常用的while(1)。這僅僅是個人習慣的問題,還是有更深層次的含義?

沒啥區別黨:都是心理作用

大部份網友認為二者並沒有什麽區別,很多時候,只是心理作用,國外工程師認為while需要經過判斷括弧裏的運算式是否非0才跳轉。但經過編譯器的精心最佳化以後,while(1)也會被最佳化成無條件跳轉(jmp指令),所以跟for(;;)沒什麽區別。

有人表示,for(;;)在英語母語者那裏很容易跟forever掛鉤。

網友解析,也很有可能是習慣問題,其實while(1),還是for(;;)兩個語法上有啥區別,那就是for(;;) 明確就是迴圈,等價於goto一直跳,沒有比較條件。

while不編譯支持最佳化的前提下都需要做cmp運算設定寄存器ZF,才能jne,je指令條件跳轉。而for(;;)就是明確的jmp無條件轉移eip,沒有jne,je條件跳轉。

不過其實無所謂的,這根本不能提高任何一點程式碼執行的效能。因為現代編譯器大多最佳化以後跟for(;;)的結果沒得什麽區別。

其實你所考慮到的一切最佳化手段,編譯器都能幫你完成,因為編譯器(尤其是開源的GCC和LLVM)是由來自全球各地的程式設計師共同研發並改進的,它們的最佳化能力遠遠強於你手動改進程式碼。

也有網友「Shuax」使用mingw編譯,實地測試一番:

for版本:

#include<stdio.h>intmain(){for(;;) {printf("for\n"); }

生成組譯:

while版本:

#include<stdio.h>intmain(){while(1) {printf("while\n"); }}

生成組譯:

你會發現,除了檔名不同,其余都相同。

當然,這裏額外說一下,不同程式碼、不同編譯器,以及不同最佳化等級,可能最終結果有所差異。

正方觀點:哪有好的編譯器

不過,有人跳出來反駁,現代編譯器的確最佳化很好,二者執行起來沒啥區別,但是實際在嵌入式工作中,尤其是MCU編程中,可沒有那麽好的編譯器。

一位工程師表示,很多嵌入式裝置只有專用的編譯器,而過去這些編譯器,尤其是嵌入式編譯器沒做好最佳化的情況下,while(1)要比for(;;)多幾個語句。

因為while裏面是判斷啊,就會變成:

label: ……mova, #1 jnzlabel

這種情況而for(;;)的話一般只會是jmp label。

許多人也有類似的經歷,並表示,有些私有編譯器連 (int)a<<0 這種都能生成非法指令,不由地懷疑配套的破芯片到底能不能受得了各種最佳化過的指令。

反方觀點:這種程式碼過時了

也有工程師呼籲,不要學習這種編碼風格,現在已經是2024年了,用for(;;)表示無限迴圈已是一種過時的風格了。

從史特勞斯特擼普博士到中國國家軍用標準,均認為 for(;;) 是一種不良風格,可參見:

  • GJB 8114-2013 R-1-9-4:無限迴圈必須使用while(1)語句,禁止使用for(;;)等其他形式

  • CppCoreGuidelines ES.73:Prefer a while-statement to a for-statement when there is no obvious loop variable

  • 360 safe rules: for語句沒有明確的迴圈變量時應改用while句語

  • 這是為什麽呢?在較為嚴格的規範體系內,for 語句專用於實作具有明確迴圈次數和迴圈變量的叠代演算法,小括弧內的三個運算式應分別專註於迴圈變量的初始化、迴圈條件的判斷、迴圈變量的增減,這樣可以使迴圈具有清晰的靜態結構,便於閱讀,利於維護。如果沒有明確的迴圈變量,則應改用 while 迴圈,避免對程式碼的維護者造成誤導。

    有人說for(;;)表示無條件迴圈,while(1)需要作條件判斷,效率比for(;;)慢,有一定道理,但那都是很早以前的事情了,現在即使沒有編譯器最佳化,這種開銷也不會成為效率的瓶頸,是不值得最佳化的,保持程式碼清晰的靜態結構更為重要!

    類似於國軍標這種嚴格的程式碼審計規則,可參見:

    github.com/Qihoo360/safe-rules

    工程師實地測試:和編譯器和最佳化有關

    公眾號博主「WKJay」也在STM32F103、ARMCC5進行過測試,將兩個邏輯分別執行一下(不開編譯器最佳化),檢視邏輯分析儀輸出的結果。

    while(1) 邏輯執行結果:

    for(;;) 邏輯執行結果:

    結果顯示,雖然迴圈體完全相同,但實際執行結果來看,for(;;) 語句執行得更快(45.863ms),比 while(1)(48.643ms) 快了5.7%左右。

    根據他的分析,for的指令更精簡,而while的指令相對更繁瑣,簡而言之,for抄了近道,而while彎彎繞繞。

    最後,他開啟了編譯器的O3最佳化,結果,二者就幾乎不存在差別了(12.505ms):

    從可讀性角度來說,while(1)簡單清晰,for(;;)就模糊多了。不過,對於一些比較老的專用編譯器來說,可能就需要慎重考慮使用哪種形式。

    對現代編譯器來說,二者完全就是一回事,更何況,高主頻的芯片不在乎一兩條機器指令了,所以這種情況下,怎麽順眼就怎麽寫。

    參考文獻

    [1] 知乎:https://www.zhihu.com/question/23043337

    [2] WKJay:極致的最佳化,while(1) 與 for(;;) 到底誰更快?.2024.1.28.

    [3] 嵌入式專欄:微控制器程式碼中while(1) 和 for(;;)有什麽區別?.2023.12.1.

    [4] 嵌入式Linux:你會用while(1)還是for(;;)寫迴圈程式碼?.2021.1.20.

    聲明: 本文素材來源網路,版權歸原作者所有。如涉及作品版權問題,請與我聯系刪除。

    <END>

    點這裏👇關註我,記得標星呀~

    感謝你的分享,點贊,在看三