[英]Why would someone define macros for first parameter including comma in C?
[英]Why would you recursively define macros in terms of other macros in C?
我想看看arduino函數digitalWrite
實際上是如何工作的。 但是當我查找函數的源代碼時,它充滿了宏,這些宏本身是根據其他宏定義的。 為什么會以這種方式構建而不是僅使用函數? 這只是糟糕的編碼風格還是在C中做事的正確方式?
例如, digitalWrite
包含宏digitalPinToPort
。
#define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) )
而pgm_read_byte
是一個宏:
#define pgm_read_byte(address_short) pgm_read_byte_near(address_short)
並且pgm_read_byte_near
是一個宏:
#define pgm_read_byte_near(address_short) __LPM((uint16_t)(address_short))
__LPM
是一個宏:
#define __LPM(addr) __LPM_classic__(addr)
__LPM_classic__
是一個宏:
#define __LPM_classic__(addr) \
(__extension__({ \
uint16_t __addr16 = (uint16_t)(addr); \
uint8_t __result; \
__asm__ __volatile__ \
( \
"lpm" "\n\t" \
"mov %0, r0" "\n\t" \
: "=r" (__result) \
: "z" (__addr16) \
: "r0" \
); \
__result; \
}))
與此沒有直接關系,但我也認為雙下划線只應由編譯器使用。 LPM
前綴是__
正確嗎?
如果您的問題是“為什么會使用多層宏?”,那么:
inline
的C99之前的時代。 一個典型的例子是20世紀80年代時代的getc
(IIRC)當時(SunOS3.2,1987)被記錄為一個宏,在man
頁中有一個注釋告訴我(我忘了細節)有一些FILE* filearr[];
getc(filearr[i++])
錯了(IIRC,當時不存在未定義的行為術語)。 當你查看一些系統頭文件(例如<stdio.h>
或它包含的某些頭文件)時,你可以找到這種宏的定義。 那個時候 (計算機只運行getc
頻率,比現在慢一千倍) ,出於效率原因, getc
必須是一個宏(因為inline
不存在,編譯器沒有像他們現在能做的那樣進行過程間優化 )。 當然,您可以在自己的宏中使用getc
。 WIFEXITED
& WEXITSTATUS
如宏,這是明智的#define
你的一些宏混合兩者。 static inline
函數(在某些頭文件中定義)轉換為等效的宏。 換句話說, #define
只有一些宏,當你無法避免它。 並明確記錄這一事實。 #ifdef macroname
測試宏,這有時很有用。 當然,當你敢於定義幾層宏(我不會稱之為“遞歸”,閱讀自引用宏 )時,你需要非常小心並理解所有這些宏的結果 (組合和單獨)。 查看預處理的表單很有幫助。
順便說一下,為了調試復雜的宏,我有時會這樣做
gcc -C -E -I/some/directory mysource.c | sed 's:^#://#:' > mysource.i
然后我查看mysource.i
,有時甚至必須編譯它,也許作為gcc -c -Wall mysource.i
來獲取位於預處理形式mysource.i
(我可以在我的emacs
編輯器中查看)。 該sed
命令是“評論”的開頭的行#
這是設置源位置(點菜#line
)......有時候我甚至做indent mysource.i
(實際上,我的Makefile
有一個特殊的規則)
LPM的前綴是__正確嗎?
順便說一句,以_
開頭的名稱(按標准,通常)保留給實現。 原則上,您不允許以_
開頭的名稱,因此不會發生任何沖突。
請注意__LPM_classic__
宏使用statement-expr擴展名 ( GCC和Clang )
另請參閱其他編程語言。 Common Lisp有一個非常不同的宏模型(更有趣的一個)。 閱讀有關衛生宏的信息 。 我個人感到遺憾的是,C預處理器從未發展成為更強大的(和Scheme類似)。 為什么沒有發生這種情況(想象一下C預處理器能夠調用Guile代碼進行宏擴展!)可能是出於社會和經濟原因。
為什么會以這種方式構建而不是僅使用函數?
目的很可能是使用前C99編譯器優化函數調用,這些編譯器不支持內聯函數(通過inline
關鍵字)。 這樣,整個類似函數的宏堆棧基本上由預處理器合並為單個代碼塊。
每次在C中調用函數時,由於跳過程序代碼,管理堆棧幀和傳遞參數,所以開銷很小。 在大多數應用程序中,此成本可以忽略不計,但如果經常調用該函數,則可能會成為性能瓶頸。
這只是糟糕的編碼風格還是在C中做事的正確方式?
很難給出明確的答案,因為編碼風格是主觀話題。 我會說,考慮使用內聯函數甚至(更好)讓編譯器自己內聯它們。 它們是類型安全的,更易讀的,更可預測的,並且在編譯器的適當幫助下,最終結果基本相同。
相關參考(它適用於C ++,但C的想法通常是相同的):
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.