簡體   English   中英

大C宏。有什么好處?

[英]Large C macros. What's the benefit?

我一直在使用一個主要由不再在公司工作的程序員編寫的大型代碼庫。 其中一位程序員顯然在他心中有一個特殊的位置,用於非常長的宏。 我可以看到使用宏的唯一好處是能夠編寫不需要在所有參數中傳遞的函數(建議在我閱讀的最佳實踐指南中使用)。 除此之外,我認為內聯函數沒有任何好處。

有些宏是如此復雜,我很難想象有人甚至寫它們。 我嘗試用這種精神創造一個,這是一場噩夢。 調試是非常困難的,因為它在調試器中將N +行代碼變為1(例如,在這個大塊代碼中的某處存在段錯誤。祝你好運!)。 我必須實際拉出宏並運行它非宏觀調試它。 我能看到這個人編寫這些內容的唯一方法就是在他調試之后用一個函數編寫的代碼自動生成它們(或者通過比我更聰明並且第一次完美地編寫它,我猜這總是可能的) 。

我錯過了什么嗎? 我瘋了嗎? 有沒有我不知道的調試技巧? 請填寫我。我真的很想聽聽觀眾中的宏觀愛好者。 :)

對我來說,宏的最佳用途是壓縮代碼並減少錯誤。 缺點顯然是在調試中,因此必須小心使用它們。

我傾向於認為如果生成的代碼不是一個數量級更小並且更不容易出錯(意味着宏處理一些簿記細節)那么它就不值得了。

在C ++中,許多這樣的用法可以用模板替換,但不是全部。 有用的宏的一個簡單示例是在MFC的事件處理程序宏中 - 沒有它們,創建事件表將更難以正確完成,而您必須編寫(和讀取)的代碼將更加復雜。

如果宏非常長,它們可能會使代碼變短但效率很高。 實際上,他可能已經使用宏來顯式內聯代碼或從運行時代碼路徑中刪除決策點。

重要的是要理解,在過去,許多編譯器並沒有完成這樣的優化,而今天我們認為理所當然的一些事情,比如快速函數調用,則無效。

對我來說,宏是邪惡的。 由於它們有如此多的副作用,而且在C ++中你可以通過內聯獲得相同的性能增益,因此它們不值得冒險。

對於前者 看到這個短宏:

#define max(a, b) ((a)>(b)?(a):(b))

然后嘗試這個電話:

max(i++, j++)

更多。 說你有

#define PLANETS 8
#define SOCCER_MIDDLE_RIGHT 8

如果拋出錯誤,它將引用'8',但不是它的任何一個有意義的表示。

我只知道做你所描述的兩個原因。

首先是強制函數內聯。 這幾乎毫無意義,因為內聯關鍵字通常做同樣的事情,而函數內聯通常是過早的微優化。

其次是用C或C ++模擬嵌套函數。 這與你的“編寫函數不需要在所有參數中傳遞”有關,但實際上可能比這更強大。 Walter Bright給出了嵌套函數有用的示例。

使用宏還有其他原因,例如使用預處理器特定的功能(如在自動生成的錯誤消息中包含__FILE____LINE__ )或以函數和模板不能的方式減少樣板代碼( Boost.Preprocessor庫在這里擅長;參見Boost.ScopeExit示例的示例枚舉代碼 ),但這些原因似乎並不適用於您所描述的內容。

非常長的宏將具有性能缺陷,例如增加的編譯二進制大小,並且肯定有其他原因不使用它們。

對於最有問題的宏,我會考慮通過預處理器運行代碼,並用函數調用(如果可能的話內聯)或直接LOC替換宏輸出。 如果存在與其他體系結構/操作系統兼容的宏,則可能會遇到困難。

部分好處是代碼復制沒有最終的維護成本 - 也就是說,不是在其他地方復制代碼,而是從中創建一個宏,只需要編輯一次......

當然,你也可以只是制作一個方法來調用,但這是更多的工作......我自己反對宏觀用法,只是試圖提出一個潛在的理由。

在C中編寫宏有很多好的理由。

一些最重要的是使用x-macros創建配置表,用於創建類似宏的函數,可以接受多個參數類型作為輸入,並將表從人類可讀/可配置/可理解的值轉換為計算機使用的值。

我不能真正看到人們編寫非常長的宏的原因,除了歷史自動函數內聯。

我會說,在調試復雜的宏時 ,(當編寫X宏等時)我傾向於預處理源文件並用預處理的文件替換原始文件。

這允許您查看生成的C代碼,並為您提供在調試器中使用的實際行。

我根本不使用宏。 內聯函數服務於宏可以執行的每個有用的目的。 宏允許你做一些非常奇怪和違反直覺的事情,比如拆分標識符(那么有人如何搜索標識符?)。

我使用過的一些遺留代碼在方法的位置上使用了非常廣泛的宏。 原因是計算機/ OS /運行時具有極小的堆棧,因此堆棧溢出是一個常見問題。 使用宏而不是方法意味着堆棧上的方法更少。

幸運的是,大部分代碼都已過時,因此現在(大部分)都已消失。

C89沒有內聯功能。 如果使用禁用擴展的編譯器(由於多種原因這是一件令人滿意的事情),那么宏可能是唯一的選擇。

盡管C99在1999年問世,但長期以來一直存在阻力。 商業編譯器供應商並不覺得值得花時間來實現C99。 有些人(例如MS)還沒有。 因此,對於許多公司來說,使用C99符合模式並不是一個可行的實際決定,即使在某些編譯器的情況下也是如此。

我已經使用了具有內聯函數擴展的C89編譯器,但是擴展程序是錯誤的(例如,當不應該存在多個定義錯誤時),這樣的事情可能會阻止程序員使用內聯函數。

另一件事是宏版本有效地強制該函數實際上將被內聯。 C99 inline關鍵字只是一個編譯器提示,編譯器可能仍然決定生成一個像非內聯函數鏈接的函數代碼的單個實例。 (如果函數不是微不足道並且返回void ,我仍然使用的一個編譯器將執行此操作)。

我還參與了一個產品,一個傳統的程序員(幸好早已離開)也與Macros有特別的戀情。 他的“定制”腳本語言是邋。的高度。 他在C語言中編寫了C ++類,這意味着所有類函數和變量都是公共的。 無論如何,他幾乎用宏觀和可變函數寫出了所有東西(世界上另一個可怕的怪物)。 所以不是寫一個合適的模板類,而是使用宏代替! 他還使用宏來創建工廠類,而不是普通的代碼......他的代碼幾乎是不可思議的。

從我所看到的,宏可以在它們很小時使用,並且以聲明方式使用,並且不包含諸如循環之類的移動部分以及其他程序流表達式。 如果宏是一行或最多兩行,並且它聲明了某事物的實例,那就沒關系。 在運行時不會破壞的東西。 宏也不應該包含類定義或函數定義。 如果宏包含需要使用調試器進入的代碼,則應刪除宏並將其替換為其他內容。

它們還可用於包裝自定義跟蹤/調試功能。 例如,您希望在調試版本中進行自定義跟蹤,而不是發布版本。

無論如何,當你在這樣的遺留代碼中工作時,一定要一次刪除一點宏的混亂。 如果你堅持下去,有足夠的時間,你最終將把它們全部刪除,讓自己的生活更輕松。 我過去做過這個,特別是凌亂的宏。 我所做的是打開編譯器開關讓預處理器生成一個輸出文件。 然后我突襲該文件,復制代碼,重新縮進,並用生成的代碼替換宏。 謝天謝地,該編譯器功能。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM