99国产精品欲av蜜臀,可以直接免费观看的AV网站,gogogo高清免费完整版,啊灬啊灬啊灬免费毛片

網易首頁 > 網易號 > 正文 申請入駐

當大模型接管編程:NASA 瘋狂的“反人類”編程要求,為何仍被奉為行業圣典?

0
分享至


整理 | 華衛

在軟件工程領域,有些 “老派” 的方法和理念,是經過時間檢驗的真理,值得我們重新審視和學習。

大多數大型軟件開發項目都會使用編碼規范,旨在規定編寫軟件的基本規則:代碼應如何構建,以及應該使用和避免哪些語言特性,尤其是在代碼的正確性會對設備產生決定性影響的領域,如潛水艇、飛機、將宇航員送上同步軌道的航天器,以及距離居民區僅幾公里之外的核電站等設施運行的控制代碼等。

在眾多編碼規范中,NASA 的編碼規則以其嚴苛性和有效性反復被提起。近期,油管博主 ThePrime Time 發布的解讀 NASA 安全編碼規則的視頻,甚至短時間內引發了超百萬觀看。

特別是在 AI 編程和“氛圍編程”流行的當下,重新審視嚴謹、可驗證的編程規范,是對軟件工程本質的回歸。

有聲音說,“老派的 NASA 編碼方式是最好的方式?!币灿腥嗽u價,“在 C 語言中使用這些標準的編碼人員是真正的戰士?!?/p>



NASA 程序員在編寫航天設備運行代碼時都遵守一套嚴格的規則,這套編碼規則由 NASA 噴氣推進實驗室(JPL)首席科學家 Gerard J. Holzmann 所提出,名為《The Power of Ten – Rules for Developing Safety Critical Code1》(十倍力量:安全關鍵代碼開發規則)。

其在開頭指出,“大多數現有的規范包含遠遠超過 100 條規則,而且有些規則的合理性存疑。有些規則,特別是那些試圖規定程序中空白使用方式的規則(提到了 Python),可能是出于個人偏好而制定的。其他一些規則則是為了防止同一組織早期編碼工作中出現的非常特定且不太可能發生的錯誤類型。毫不奇怪,現有編碼規范對開發人員實際編寫代碼的行為影響甚微。許多規范最致命的方面是它們很少允許進行全面的基于工具的合規性檢查?;诠ぞ叩臋z查很重要,因為對于大型應用程序編寫的數十萬行代碼,手動審查通常是不可行的?!?/p>

ThePrime Time 對此表達了強烈地贊同,稱“確實有很多個人偏好被寫入了代碼規范中。我認同目前提到的所有內容,代碼就應該可靠。自動化和工具的使用應該杜絕個人偏好?!?/p>

NASA 的編碼規則主要針對 C 語言,力求優化更全面檢查用 C 語言編寫的關鍵應用程序可靠性的能力。原因是,“在包括 JPL 在內的許多組織中,關鍵代碼都是用 C 語言編寫的。由于其悠久的歷史,這種語言有廣泛的工具支持,包括強大的源代碼分析器、邏輯模型提取器、度量工具、調試器、測試支持工具,以及成熟穩定的編譯器選擇。因此,C 語言也是大多數已開發的編碼規范的目標?!?/p>

ThePrime Time 表示,“我知道現在有很多軟件開發人員,一聽到用 C 語言編寫安全關鍵代碼,可能就會想‘怎么又是這個’ 。你們可沒有像 ‘旅行者號’ (NASA 研制的太空探測器)那樣的項目,你們還不是頂尖開發者?!?/p>

此外,Holzmann 認為,“為了有效,規則集必須很小,并且必須足夠清晰,以便于理解和記憶。規則必須足夠具體,以便可以機械地進行檢查。當然,這么小的規則集不可能涵蓋所有情況,但它可以為我們提供一個立足點,對軟件的可靠性和可驗證性產生可衡量的影響?!币虼耍麑?NASA 的編碼規則限制在十條。

這十條規則正在 NASA 噴氣推進實驗室用于關鍵任務軟件的編寫實驗,取得了令人鼓舞的成果。據 Holzmann 介紹,一開始,NASA 的開發人員對遵守如此嚴格的限制存在合理的抵觸情緒,但克服之后,他們常常發現,遵守這些規則確實有助于提高代碼的清晰度、可分析性和安全性。這些規則減輕了開發人員和測試人員通過其他方式確定代碼關鍵屬性(例如終止性、有界性、內存和棧的安全使用等)的負擔。“這些規則就像汽車上的安全帶,起初可能有點讓人不舒服,但過一段時間后,使用它們會成為習慣,不使用反而難以想象?!?/p>

ThePrime Time 最后對 NASA 編碼規則給出的整體評價是,“我喜歡這份文檔,即便我并非完全認同其中所有的規則。我只是很驚訝,政府機構編寫的內容竟如此條理清晰。這是一份極其連貫的文檔,似乎出自一位追求務實的人之手?!?/p>

不少與 NASA 工程師共事過的開發者們,都對這則 NASA 十大編碼規則的解讀視頻深有感觸:“他們的編碼指南并不‘瘋狂’,反而實際上相當理智。我們沒有以這種方式編程才是瘋狂的”,并分享了許多個人的相關經歷。


“在學習 C 語言的時候,我的教授曾為衛星編寫 C 程序 / 代碼。他把自己的方法教給了我們,這種方法要求我們在電腦上編程之前,先把所有內容都寫在紙上。這種方式迫使我們準確理解自己正在編寫的內容、內存分配等知識,還能編寫出更高效的代碼,并掌握相關知識。我很慶幸自己是通過這種方式學習的,因為在面試時,我能輕松地在白板上編寫代碼?!币幻こ處熣f。

另一位與前 NASA 工程師共同開發過游戲的程序員透露,“他的代碼是我見過的最整潔、最易讀的。當時我還是一名初級程序員,僅僅通過和他一起編寫代碼,我就學到了很多東西。我們使用的是 C++ 語言,但他的編程風格更像是帶有類的 C 語言。他的代碼本身就很易于理解(具有自解釋性),不過他仍然對自己的代碼進行了注釋(既有代碼中的注釋,也有實際的文檔說明)?!?/p>

還有一位自述“和 NASA 一位級別很高的程序員關系非常密切”的開發者表示,“我聽過很多故事,這些故事都能說明制定所有這些標準的合理性??陀^來講,從 Java 1.5 升級到 1.7 的成本,比從零開始重建任務控制中心(MCC)還要高。而重建任務控制中心是用 C 語言完成的,其中另一位首席工程師曾是 C++ 專家,他認定最初的 C 語言更可靠?!?/p>

同時有前 NASA 工程師出來現身說法道,“曾參與構建云基礎設施,他們的指導原則可不是鬧著玩的,代碼審查簡直是人間煉獄?!畤揽痢@個詞用來形容再貼切不過了。不過,相比我之前在電信、金融科技領域的工作經歷,以及后來在其他科技公司的工作,我在 NASA 工作期間對可靠性方面的了解要多得多。”

“NASA 的編碼要求太瘋狂了”

對于這十條規則,Holzmann 已經聲明,“為了支持強大的審查,這些規則有些嚴格,甚至可以說嚴苛。但這種權衡是有道理的。在關鍵時候,尤其是開發安全關鍵代碼時,多費些功夫,遵守更嚴格的限制是值得的。這樣我們就能更有力地證明關鍵軟件能按預期運行。”并且,每條規則之后都附了其被納入的簡短理由。

ThePrime Time 對這些編碼規則及理由一一進行了評價和分析,以下是經不改變原意的翻譯和編輯后整理出來的解讀內容。

規則一:

將所有代碼限制在非常簡單的控制流結構中,不要使用 goto 語句、setjmp 或 longjmp 結構以及直接或間接遞歸。

理由:更簡單的控制流意味著更強的驗證能力,并且通常能提高代碼的清晰度。禁止遞歸可能是這里最讓人意外的一點。不過,如果沒有遞歸,我們就能保證有一個無環的函數調用圖,這能被代碼分析器利用,還能直接幫助證明所有本應有限的執行實際上都是有限的。(注意,這條規則并不要求所有函數都有一個單一的返回點——盡管這通常也會簡化控制流。不過,有很多情況下,提前返回錯誤是更簡單的解決方案。)

ThePrime Time:我不知道間接遞歸是什么意思。間接遞歸是指兩個函數相互調用嗎?在實際情況中,這種情況確實會發生,而且發生過很多次。比如有一個函數需要調用另一個函數去做某些事情并進行一些檢查,然后再通過某種不受你控制的方式返回結果。特別是在那些沒有異步 / 等待(async/await)機制的語言里,我猜你只能阻塞線程了,對吧?規則是說如果沒有異步機制,就只能阻塞線程,然后在一個while循環里處理,是這樣嗎?我得想想我自己使用間接遞歸的場景。實際上,我在處理套接字重連時就用到了間接遞歸。具體來說,當我打開一個套接字(就像這樣,打開這個套接字),重連機制會調用一個私有函數來重置狀態,然后調用reconnect函數,reconnect函數會調用connect函數,當連接斷開時,connect函數又會再次調用自己。從技術上講,這就是一種間接遞歸。我在想,也許我可以把它改成用while循環加上等待機制,這樣會不會更簡單呢?現在真的讓我開始思考這個問題了,這可能只是一種替代方案。

哎呀,我已經違反規則一了。不過間接遞歸確實是一種非常強大的無限問題解決機制,但我可以嘗試不用它,對吧?我完全不介意嘗試新的做法。理由很簡單,“更簡單的控制流意味著更強的驗證能力,并且通常能提高代碼的清晰度?!蔽艺J同這一點,確實看到代碼里有類似這樣的邏輯時會覺得有點繞。比如在一個 close 函數中調用 reconnect,然后在 reconnect 中檢查是否已經啟動,如果沒有完成,就返回。這些語句的順序會導致問題,所以我可以理解為什么會出現這種情況。我甚至可以說服自己接受這一點。

說實話,這是一個非常務實的觀點,解釋了為什么不應該使用遞歸。而且說句公道話,我大學三四年級的時候(不對,其實是大二),老師讓我們用非遞歸的方式實現 AVL 樹。如果你不熟悉 AVL 樹,這是一種使用旋轉的自平衡二叉搜索樹,有四種不同的旋轉:右旋、左旋、先右后左旋和先左后右旋。做起來其實很有趣,假設我們有一個二叉樹,像這樣:a(根節點),b 是左子節點,c 是右子節點。我們想重新組織成 b 作為根節點,a 作為左子節點,c 作為右子節點。如果你有一棵二叉樹,看起來像 “a b c”,你就把它重組為 “a c b”,然后進行旋轉。很簡單直接,對吧?老師說我們要實現這個程序,但不能用遞歸。這對我來說是一次很棒的學習經歷。

規則二:

所有循環必須有固定的上限。檢查工具必須能夠輕易地靜態證明循環的預設迭代上限不會被突破。如果無法靜態證明循環的上限,就視為違反規則。

理由:沒有遞歸且存在循環上限可以防止代碼失控。當然,這條規則不適用于那些本就不打算終止的迭代(例如在進程調度器中)。在這些特殊情況下,適用相反的規則:必須能靜態證明迭代不會終止。支持這條規則的一種方法是,給所有迭代次數可變的循環添加明確的上限(比如遍歷鏈表的代碼)。當超過上限時,會觸發斷言失敗,包含失敗迭代的函數將返回錯誤。(關于斷言的使用,請參見規則 5)

ThePrime Time:這是不是意味著不能使用像數組的forEach這樣的方法呢?因為從技術上講,其上限是根據數組動態變化的,沒有固定值。還是說不能使用while (true)這種循環呢?這是一條有趣的規則。

規則三:

初始化后不要使用動態內存分配。

理由:這條規則在安全關鍵軟件中很常見,并且出現在大多數編碼規范里。原因很簡單:像 malloc 這樣的內存分配器和垃圾回收器,其行為往往不可預測,可能會對性能產生重大影響。一類顯著的編碼錯誤也源于對內存分配和釋放例程的不當處理,比如忘記釋放內存、釋放后繼續使用內存、試圖分配超過實際可用的內存,以及越界訪問已分配的內存等等。強制所有應用程序在固定的、預先分配好的內存區域內運行,可以避免很多這類問題,也更容易驗證內存的使用情況。需要注意的是,在不使用堆內存分配的情況下,動態申請內存的唯一方法是使用棧內存。在沒有遞歸的情況下(規則 1),可以靜態推導出棧內存使用的上限,從而可以證明應用程序將始終在其預先分配的內存范圍內運行。

ThePrime Time:這么一來,JavaScript 和 Go 語言可就有點麻煩了。不過說實在的,其實在 JavaScript 和 Go 里也能做到這一點。顯然,在大多數情況下是可以的。但我敢肯定,只要調用類似G Funk(這里可能是隨意提及的某個函數)這樣的函數,它背地里肯定會分配一些你不知道的內存。當然,不是所有解釋型語言都這樣,這么說不太準確,不是所有解釋型語言都有這個問題。

我猜這條規則意味著不能使用閉包,對吧?因為閉包會涉及到內存分配。準確地說,你得使用內存池(Arena)進行內存分配。也就是說,不能隨意使用列表或字符串嗎?也不是,其實可以使用列表和字符串,只是意味著所有內存分配都必須在程序開始時完成。我猜這里說的是堆內存,而不是棧內存。另外,我是這么理解的,比如說你從服務器獲取一系列響應數據,你得事先分配一塊足夠大的內存區域,用來存儲所有可能的響應數據,然后像使用環形緩沖區一樣循環利用這塊內存,這樣就不會有額外的內存分配操作了,所有可能用到的數據都已經預先分配好了。所以一開始你就應該擁有所需的所有內存,這意味著可以使用字符串,只是得預先定義好字符串占用內存的大小。天吶,這得好好琢磨琢磨,確實很費腦筋,不過環形緩沖區的概念真的很有意思。

“在不使用堆內存分配的情況下,動態申請內存的唯一方式是使用棧內存。根據規則一,在沒有遞歸的情況下,可以靜態推導出棧內存使用的上限,這樣就能證明應用程序始終在其預先分配的內存范圍內運行。”這聽起來有點瘋狂,但其實挺酷的,仔細想想還挺有道理。

我聽說在游戲開發里有這樣一種做法,如果我說錯了也請大家指正。在游戲開發中,每個子團隊會有各自的資源預算,包括內存和 CPU 時間。在你負責的程序部分,你只能使用分配給你的那部分資源。一旦超出預算,就會有類似這樣的提示:“嘿,物理模擬團隊,你們用的時間太多了,能不能想想辦法?” 我覺得這聽起來挺不錯的。我知道有這么回事,我舉的這個例子是希望普通的游戲開發者也能理解。就好比今年兩家小的游戲工作室因為資源超支沒拿到獎金,大致就是這么個情況。寬泛來講,實際情況比在 Twitch 聊天里說的要復雜一些,但差不多就是這樣。我只是以一個普通游戲開發者的角度來解釋這個規則,我開發過一些小游戲,但我也知道自己還算不上專業的游戲開發者。

規則四:

任何函數的長度都不應超過以標準參考格式打印在一張紙上的長度,即每行寫一條語句、每行寫一個聲明。通常情況下,這意味著每個函數的代碼行數不應超過 60 行。

理由:每個函數都應該是代碼中的一個邏輯單元,可以作為一個單元來理解和驗證。跨越計算機顯示器多個屏幕或打印時多頁的邏輯單元要難得多。過長的函數往往是代碼結構不佳的表現。

ThePrime Time:好的,這挺合理的。60 行代碼的空間足夠你把事情弄清楚了。鮑勃大叔(Uncle Bob ,著名編程大師 Robert C. Martin)規定每個函數一般只能有三到五行代碼,相比之下 60 行代碼算很多了。不過這里說的是打印在一張紙上,對吧?就是說代碼打印在單張紙上,大概就是這個意思。注意,這其實也不算特別嚴格的硬性規定,但從能打印在紙上這個角度來說,它又算是個硬性規定。我覺得 60 行代碼能表達很多內容,肯定有辦法打破這個規則,但我感覺自己通常能輕松寫出最多 60 行代碼的函數,我覺得這一點都不難。沒錯,Ghost 標準庫有數千行代碼,但我不會把 Ghost 標準庫當作史上最整潔、最出色的代碼之一。老實說,我個人覺得 Ghost 標準庫讀起來真的很糟糕。

這條規則的理由是,每個函數都應該是代碼中的一個邏輯單元,能夠作為一個整體被理解和驗證。如果一個邏輯單元跨越計算機顯示器的多個屏幕,或者打印出來有好多頁,理解起來就困難得多。過長的函數往往意味著代碼結構不佳。我基本上同意這個觀點,我覺得實際上很少能見到超過 60 行代碼的函數。而且一般來說,當你遇到這樣的函數時,要么是因為功能本身非常復雜,由于行為的關聯性必須寫在一起;要么這個函數寫得很糟糕。除非你寫 React 代碼,我覺得我說的這些還是適用的。

規則五:

代碼的斷言密度平均每個函數至少應有兩個斷言。斷言用于檢查在實際執行中不應發生的異常情況。斷言必須始終無副作用,并且應定義為布爾測試。當斷言失敗時,必須采取明確的恢復措施,例如,向執行失敗斷言的函數的調用者返回錯誤條件。任何靜態檢查工具能夠證明永遠不會失敗或永遠不會成立的斷言都違反此規則。(也就是說,不能通過添加無用的 “assert (true)” 語句來滿足該規則。)

理由:工業編碼工作的統計數據表明,單元測試通常每編寫 10 到 100 行代碼就能發現至少一個缺陷。斷言密度越高,攔截缺陷的幾率就越大。斷言的使用通常也被推薦作為強防御性編碼策略的一部分。斷言可用于驗證函數的前置和后置條件、參數值、函數返回值以及循環不變式。由于斷言無副作用,因此在測試后,可以在對性能關鍵的代碼中有選擇地禁用它們。

ThePrime Time:我很喜歡這條規則。我覺得它有很多優點,而且我覺得自己需要更多地實踐這條規則。我還得繼續堅持,因為就像我之前說的,我的代碼庫里已經有不少斷言了。我確實經常使用斷言,但目前我代碼里的斷言一旦觸發,程序就會直接崩潰。比如說,當程序內部生成的消息與我預期的不一致時,我就會認為自己犯了嚴重錯誤,覺得整個程序都得“炸掉” ,結果整個程序就會受到影響。有一點很棒,如果你能想出某種模糊測試策略,就能測試你的程序。你可以往程序里輸入一堆隨機數據,而程序里應該有防御性的語句,以確保不會觸發更多的斷言。這樣一來,你甚至可以減少很多針對模糊測試的特定單元測試,這是不是很神奇?

下面是一個典型的斷言使用示例:如果條件 C 成立,且斷言 p 大于等于為真,就返回錯誤。假設斷言定義如下:定義一個名為c_assert的斷言,用于調試,當斷言失敗時,輸出文件和行號等信息,這里設置為false。在這個定義中,fileline由宏預處理器預先定義,用于輸出失敗斷言所在的文件名和行號。語法#e會將斷言條件e轉換為字符串,作為錯誤消息的一部分打印出來。在嵌入式程序代碼中,通常沒有地方打印錯誤消息,在這種情況下,對測試調試的調用會變成空操作,斷言就變成了純粹的布爾測試。這有助于從異常行為中恢復錯誤。

我開始接觸這 “十條規則” 的契機是,有個來自 Tiger Beetle(一個項目)的人加入了我們。他們在 Tiger Beetle 項目中對斷言的使用非常嚴格。他可以往 Tiger Beetle 里輸入任何數據,而程序始終能正常運行。他們每天在每次構建時,都會進行相當于 200 年查詢量的測試,程序不斷接受大量測試,而且運行得非常穩定。這真是個超酷的項目。

規則六:

數據對象必須在盡可能小的作用域級別聲明。

理由:這條規則支持數據隱藏的基本原則。顯然,如果一個對象不在作用域內,其值就不能被引用或破壞。同樣,如果必須診斷一個對象的錯誤值,可能分配該值的語句越少,診斷問題就越容易。該規則不鼓勵將變量重復用于多個不兼容的目的,這可能會使故障診斷復雜化。

ThePrime Time:有人說這只是因為 C 語言沒有恰當的錯誤處理和語法特性,我強烈反對這種說法。實際上,斷言非常有用。比如說,Tiger Beetle 項目是用 Zig 語言編寫的,Zig 有恰當的錯誤處理工具,它有結果對象,而且其自身的錯誤處理結果對象能提供比標準錯誤更好的堆棧跟蹤信息,這真的很酷。我記得在采訪時,他說當時 Tiger Beetle 項目里有 8000 個斷言。

沒錯,斷言不是錯誤處理機制,實際上斷言不是用于處理錯誤的,它是一種不變式,可以說是硬性終止條件。我們來看看,這和在 Zig 語言里直接返回錯誤有什么不同呢?在 Zig 里,你可以返回一個錯誤或者一個值,但這就是一種錯誤處理方式。Zig 里不會硬性終止程序,它必須有恢復機制。我覺得這樣也挺好,無論是硬性終止,還是軟性終止并搭配某種錯誤恢復機制,都需要構建相應的錯誤恢復機制。

我其實并沒有完全理解規則六,除了感覺好像是說在使用變量的地方定義它,這樣作用域就是最小的,是這個意思嗎?聽起來好像是這樣,這里是在說封裝的概念嗎?

規則七:

非 void 函數的返回值必須由每個調用函數檢查,并且每個函數內部必須檢查參數的有效性。

理由:這可能是最常被違反的規則,因此作為一般規則有些可疑。從嚴格意義上講,這條規則意味著即使是 printf 語句和文件關閉語句的返回值也必須被檢查。不過也有觀點認為,如果對錯誤的響應與對成功的響應沒有區別,那么顯式檢查返回值就沒什么意義。這通常是調用 printf 和 close 的情況。在這種情況下,可以接受將函數返回值顯式轉換為 (void)—— 這表明程序員是有意忽略返回值,而非不小心遺漏。在更可疑的情況下,應該有注釋解釋為什么返回值無關緊要。不過,在大多數情況下,函數的返回值不應被忽略,尤其是在必須將錯誤返回值沿函數調用鏈向上傳播的情況下。標準庫因違反此規則而臭名昭著,并可能導致嚴重后果。例如,如果不小心執行 strlen (0),或者使用標準 C 字符串庫執行 strcat (s1, s2, -1)—— 結果就很糟糕。遵循這條通用規則,我們可以確保例外情況必須有合理的解釋,并且機械檢查器會標記違規行為。通常,遵守這條規則比解釋為什么不符合規則更容易。

ThePrime Time:這實際上是我非常喜歡 Zig 的一個原因。我想 Rust 語言也有類似的情況,只不過當你忽略返回的結果或異步操作的返回值時,Rust 只是給出警告。我喜歡這條規則,我覺得這是一條很棒的規則。這是一條普遍適用的好規則。要知道,編程的很大一部分就是學習這些技巧,避免自己給自己挖坑。

規則八:

預處理器的使用必須僅限于包含頭文件和簡單的宏定義。不允許使用令牌粘貼、可變參數列表(省略號)和遞歸宏調用。所有宏必須展開為完整的語法單元。條件編譯指令的使用通常也值得懷疑,但并非總是可以避免。這意味著,即使在大型軟件開發項目中,除了避免同一頭文件多次包含的標準樣板代碼外,也很少有理由使用超過一兩個條件編譯指令。每次此類使用都應通過基于工具的檢查器標記,并在代碼中說明理由。

理由:C 預處理器是一個強大的混淆工具,可能會破壞代碼的清晰度,并使許多基于文本的檢查器感到困惑。即使手頭有正式的語言定義,不受限制的預處理器代碼中的構造的效果也可能極其難以破譯。在 C 預處理器的新實現中,開發人員通常不得不求助于使用早期實現作為解釋 C 標準中復雜定義語言的裁判。對條件編譯持謹慎態度的理由同樣重要。請注意,僅使用十個條件編譯指令,就可能有多達 2 的 10 次方種可能的代碼版本,每種版本都必須進行測試 —— 導致所需的測試工作量大幅增加。

ThePrime Time:我認為對預處理宏保持謹慎肯定沒錯。我理解代碼時遇到的困難,沒有比處理預處理宏更多的了。預處理宏真的是最難懂的部分之一。這條規則實際上會讓我們的開發工作變得更加復雜,我一點都不喜歡。有意思的是,總體來說我不喜歡預處理器。我能理解預處理器肯定會引發一堆問題,人們通常把它們叫做宏。一般來說,宏可能很有用,但它們通常也非常難以理解,理解起來特別費勁。謝天謝地 C 語言沒有宏(這里表述有誤,C 語言有宏,作者可能想表達宏的復雜性讓人頭疼 )。宏是一種強大的工具,但就像所有強大的工具一樣,它們非常危險。預處理器是一個強大的混淆工具,會破壞代碼的清晰度,讓許多基于文本的檢查器感到困惑。即使手頭有正式的語言定義,不受限制的預處理代碼中的結構效果也極難解讀。在 C 預處理器的新實現中,開發人員常常不得不借助早期的實現來解讀 C 標準中的復雜定義語言。

對條件編譯保持謹慎的理由同樣重要。要知道,僅僅 10 個條件編譯指令就可能產生多達 2 的 10 次方種代碼版本,每個版本都必須進行測試,這會大幅增加所需的測試工作量。我是說,這一點非常關鍵。一般來說,條件編譯就是一場噩夢,盡管有時又不得不使用它。我覺得這條規則務實的地方在于,它認識到雖然無法避免使用條件編譯,但條件編譯確實非常困難且麻煩。

沒錯,我猜 Rust 語言的 cargo 特性主要是因為 Rust 編譯速度慢才存在的。我認識的大多數人對 Rust 二進制文件不太感興趣,更多的是擔心編譯時間太慢。我敢肯定,最終 Rust 二進制文件非常重要,但就我所知,很多時候問題就出在編譯速度上。正是因為編譯慢,才引出了一系列問題,比如我總是會遇到這樣的情況,使用 clap 時忘記添加 feature derive,然后又得去添加;使用 request 時又忘記添加 request feature 之類的,情況越來越糟。你們看過 AutoSAR 的 C 代碼嗎?我敢肯定那代碼很糟糕。

規則九:

指針的使用應受到限制。具體來說,允許的解引用級別不超過一級。指針解引用操作不得隱藏在宏定義或 typedef 聲明中。不允許使用函數指針。

理由:即使是經驗豐富的程序員也容易誤用指針。它們可能使程序中的數據流難以跟蹤或分析,尤其是對于基于工具的靜態分析器。同樣,函數指針可能會嚴重限制靜態分析器可以執行的檢查類型,只有在有充分理由使用它們的情況下才應使用,并且理想情況下應提供替代方法來幫助基于工具的檢查器確定控制流和函數調用層次結構。例如,如果使用函數指針,工具可能無法證明沒有遞歸,因此必須提供替代保證來彌補分析能力的損失。

ThePrime Time:比如說,怎么處理異步相關的操作和中斷呢?我覺得他們可能不處理異步操作,但我又確定他們肯定會處理。處理異步操作肯定得使用某種互斥鎖,對吧?比如使用信號量互斥鎖,然后還得涉及對內存的引用。異步操作具有不確定性,所以不太好處理。其實也不是完全不確定,只是你得在一定程度上進行處理。想象一下,你有一個探測器,上面有個小攝像頭正在拍照。在某個時刻你拍了照,照片進行處理后存儲到內存中,處理完成后會有提示。然后某些代碼需要被喚醒,這不也在一定程度上涉及到函數指針嗎?我猜得用互斥鎖,對吧?所以我猜你會有一段代碼,比如說處理拍照的代碼。你得在這里進行一些操作,比如獲取信號量,在 C 語言里信號量的值為 1,具體是用 lock unlock(鎖定解鎖 )還是 lock acquire(獲取鎖 )我記不清了。代碼就停在這里等待,當照片數據傳入后,代碼開始處理,處理完之后再回到等待狀態。

我理解這個,不過這條規則感覺更難落實。

規則十:

所有代碼從開發的第一天起,就必須在編譯器最嚴格的設置下啟用所有編譯器警告進行編譯。所有代碼必須在此設置下編譯且不發出任何警告。所有代碼必須每天至少使用一個,但最好是多個最先進的靜態源代碼分析器進行檢查,并且應以零警告通過分析。

理由:如今市場上有幾種非常有效的靜態源代碼分析器,還有相當多的免費工具。任何軟件開發工作都沒有理由不使用這種現成的技術。即使對于非關鍵代碼的開發,也應將其視為常規做法。零警告規則甚至適用于編譯器或靜態分析器給出錯誤警告的情況:如果編譯器或靜態分析器感到困惑,應重寫導致困惑的代碼,使其更簡單有效。很多開發者一開始認為某個警告肯定是無效的,結果后來才意識到,由于一些不那么明顯的原因,該警告實際上是合理的。早期的靜態分析器,比如 lint,大多會給出無效的提示信息,這讓靜態分析器的名聲有些不好,但現在情況已經不同了。當今最好的靜態分析器速度快,并且會生成有針對性且準確的提示消息。在任何一個嚴肅的軟件項目中,它們的使用都不應有商量余地。

ThePrime Time:說實話,我覺得這挺合理的。尤其是對于新手而言,如果你剛開始接觸軟件開發,就應該能夠做到這一點。公平地說,我不算專業的軟件開發人員,所以我能理解這一點。我覺得這真的很棒,規則 10 簡直太實用了。

不過,要把這條規則應用到很多項目中可能會很困難。比如說在 JavaScript 開發中,大家都知道 JavaScript 的 lint 工具體驗很差,大多數 ESLint 規則純粹是些主觀的好壞評判標準。比如在處理 Promise 時,使用rereject作為參數實際上是不好的做法,對吧?

https://www.youtube.com/watch?v=JWKadu0ks20

聲明:本文為 InfoQ 整理,不代表平臺觀點,未經許可禁止轉載。

會議推薦

AICon 2025 強勢來襲,5 月上海站、6 月北京站,雙城聯動,全覽 AI 技術前沿和行業落地。大會聚焦技術與應用深度融合,匯聚 AI Agent、多模態、場景應用、大模型架構創新、智能數據基建、AI 產品設計和出海策略等話題。即刻掃碼購票,一同探索 AI 應用邊界!

今日薦文

你也「在看」嗎?

特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相關推薦
熱點推薦
千元鈦杯為何走紅?膳魔師、富光、小米等入局,近半年出廠價腰斬

千元鈦杯為何走紅?膳魔師、富光、小米等入局,近半年出廠價腰斬

紅星資本局
2025-05-21 16:36:14
成本2元,賣價19800元!央視最新曝光,不少人被騙,趕緊別用了

成本2元,賣價19800元!央視最新曝光,不少人被騙,趕緊別用了

逍遙史記
2025-05-21 10:50:45
65歲火箭專家娶29歲張嚴平,張嚴平父母得知男方身份后驚呆了。

65歲火箭專家娶29歲張嚴平,張嚴平父母得知男方身份后驚呆了。

百態人間
2025-05-20 16:26:40
新教皇是個猛人!

新教皇是個猛人!

難得君
2025-05-19 13:00:43
聽我一句勸,最多十年燃油車將面臨和新能源車一樣的窘境:加油難

聽我一句勸,最多十年燃油車將面臨和新能源車一樣的窘境:加油難

李子櫥
2025-05-21 20:16:49
鳳姐最新訪談:40歲牙齒脫落后悔出名,認為當初被電視節目糟蹋了

鳳姐最新訪談:40歲牙齒脫落后悔出名,認為當初被電視節目糟蹋了

漢史趣聞
2025-05-20 11:31:53
持續40+分鐘的低強度慢跑,對身體而言意味著什么?

持續40+分鐘的低強度慢跑,對身體而言意味著什么?

解說阿洎
2025-05-21 00:14:01
1-0!熱刺奪冠多神奇?全場3射1正 破71年紀錄  孫興慜俱樂部首冠

1-0!熱刺奪冠多神奇?全場3射1正 破71年紀錄 孫興慜俱樂部首冠

狍子歪解體壇
2025-05-22 04:58:15
15年前把狼當兒子養的四川女孩,10年后與它相認,結局卻無比唏噓

15年前把狼當兒子養的四川女孩,10年后與它相認,結局卻無比唏噓

蘇星河
2025-05-21 20:06:25
黃圣依是欠了錢還是被樣子威脅了,很難想象她同意拍這套圖

黃圣依是欠了錢還是被樣子威脅了,很難想象她同意拍這套圖

可樂談情感
2025-05-21 11:59:01
北京時間5月22日中央5臺直播表:CCTV5、CCTV5+節目單!

北京時間5月22日中央5臺直播表:CCTV5、CCTV5+節目單!

天光破云來
2025-05-22 06:53:58
朱媛媛到底患啥癌 抗癌五年不掉發不消瘦拍戲逛街 黃曉明發文炸了

朱媛媛到底患啥癌 抗癌五年不掉發不消瘦拍戲逛街 黃曉明發文炸了

野山歷史
2025-05-22 09:19:46
北航一保研學生跳樓自殺:導師臨時換題,被迫延畢,更多細節流出

北航一保研學生跳樓自殺:導師臨時換題,被迫延畢,更多細節流出

娜烏和西卡
2025-05-21 13:14:25
那年,成都少婦與黑人,視頻全網瘋傳!

那年,成都少婦與黑人,視頻全網瘋傳!

倉一胤
2025-05-16 23:19:32
愛德華茲因在賽后發布會使用不文明語言被NBA罰款50000美金

愛德華茲因在賽后發布會使用不文明語言被NBA罰款50000美金

雷速體育
2025-05-22 09:37:24
10將落選本期國足集訓!武磊回歸楊明洋成唯一新人,國安第一大戶

10將落選本期國足集訓!武磊回歸楊明洋成唯一新人,國安第一大戶

雷速體育
2025-05-22 09:58:19
“中年油膩女”的標準來了:當女人步入45歲,基本都中招,自查后扎心了

“中年油膩女”的標準來了:當女人步入45歲,基本都中招,自查后扎心了

青州融媒
2025-05-21 17:32:01
慶祝奪冠!熱刺官方社媒曬圖片:距離熱刺上次奪冠已過去0天

慶祝奪冠!熱刺官方社媒曬圖片:距離熱刺上次奪冠已過去0天

直播吧
2025-05-22 05:43:31
汪小菲罵S媽聊天記錄曝光,句句在理,大S經紀人最后發聲也公開!

汪小菲罵S媽聊天記錄曝光,句句在理,大S經紀人最后發聲也公開!

古希臘掌管月桂的神
2025-05-21 16:58:45
49歲立威廉到成都旅游,臉部痘印多,頭上有禿斑,打扮像霸道總裁

49歲立威廉到成都旅游,臉部痘印多,頭上有禿斑,打扮像霸道總裁

鑫鑫說說
2025-05-22 09:31:24
2025-05-22 11:36:49
AI前線 incentive-icons
AI前線
面向AI愛好者、開發者和科學家,提供AI領域技術資訊。
856文章數 80關注度
往期回顧 全部

科技要聞

蘋果設計靈魂投奔OpenAI 十年淘汰iPhone?

頭條要聞

牛彈琴:南非總統穿西裝參加鴻門宴 他比澤連斯基老練

頭條要聞

牛彈琴:南非總統穿西裝參加鴻門宴 他比澤連斯基老練

體育要聞

威少被交易時,雷霆下一個MVP已在陣中

娛樂要聞

朱媛媛喪事一切從簡,親戚剛知她去世

財經要聞

中國,拋售美債!

汽車要聞

或超700馬力 保時捷911 GT2 RS最新消息曝光

態度原創

家居
親子
本地
公開課
軍事航空

家居要聞

黑白簡約 見證平凡的蛻變

親子要聞

誰教你的啊

本地新聞

云游中國 |重慶人手一只熊貓?四世同堂等你打卡

公開課

李玫瑾:為什么性格比能力更重要?

軍事要聞

巴基斯坦與印度互相驅逐對方一名外交官

無障礙瀏覽 進入關懷版 主站蜘蛛池模板: 铜梁县| 修水县| 乌拉特中旗| 梓潼县| 义乌市| 杂多县| 平舆县| 元阳县| 湟源县| 临西县| 英山县| 怀来县| 正安县| 屏东县| 得荣县| 大英县| 霍邱县| 内乡县| 通州市| 汝阳县| 奉新县| 彰武县| 哈密市| 大邑县| 鹤峰县| 邵武市| 建始县| 元氏县| 九寨沟县| 竹山县| 阿拉善盟| 区。| 时尚| 美姑县| 临汾市| 乃东县| 桓台县| 酒泉市| 陇西县| 康定县| 勃利县|