回覆列表
-
1 # IT劉小虎
-
2 # TonyDeng
很明顯你沒弄明白宏的工作機制。解釋太長,不詳細論述,只告訴你關鍵點——宏只是文字替換,它工作在編譯之前,此時語言語法還沒起作用。
自己領悟吧。C++之父很討厭C的宏機制,不妨去看看他說的理由。到今時今日,C編譯器的功能進化許多,還有人這麼崇拜“宏函式”,也算是華人特色。
-
3 # 日衝資訊 黃
簡單地說,宏就是給一段程式碼起個名字,在預編譯階段,用到宏定義的地方會被替換成程式碼,再進行編譯。還有一些被稱為編譯開關的東東,符合某些條件時可以增加或去掉部分程式碼。
總之宏定義,是控制預編譯器用的。我個人很討厭這個東西,它沒有多大用途,卻很容易把程式碼搞得亂七八糟的看都看不懂。
先說結論,並不是所有的函式式宏定義都能方便地(請注意“方便地”這個詞)使用普通函式替換的,題主這麼問,相信也是知道這一點的。
我的上個回答介紹了利用宏定義為C語言程式實現一套“超時機制”,這套機制沒有使用普通函式,就是因為宏能夠帶來極大的便捷性。
不過遺憾的是,現在還在稽核中(已經超過36小時了)。避免“繁瑣”的程式碼不過,C語言中的“超時”機制並不難實現,上一個回答利用 usleep() 函式就建立了一套非常簡易的“超時”功能,鑑於還沒有稽核透過,相關的C語言程式碼這裡再寫一次,請看:
上述C語言程式碼將阻塞等待 ready 位,但是並不會無限等待下去,而是最多等待 5000ms(即 5 秒)。這麼處理雖然比較粗糙,但是的確能夠解決“無限等待”問題,只不過僅僅等待一個 ready 位就需要寫 3 行程式碼,如果需要做“超時”處理的地方比較多,整個C語言程式碼看起來就顯得非常囉嗦了。
要是算上“超時”判斷語句 if(get_cur_ms()-otime >= 5000)的話,就需要至少 4 行程式碼了。
程式設計師小明想到了將上面略微繁瑣的“超時”C語言程式碼封裝成函式,他想:封裝後,以後若是想使用“超時”功能的話,只需一行函式呼叫就可以了,於是寫出了類似下面這樣的C語言程式碼:
小明定義的 cond_timeout() 函式接收兩個引數:cond 引數表示需要等待的條件,timeout 引數表示最多等待的時間(單位ms)。如果在 timeout 時間內 cond 條件仍然沒有成立,則 cond_timeout() 函式返回 1 表示“等待 cond 已超時”,否則返回 0 表示“成功等待到了 cond 條件”。
定義好 cond_timeout() 函式後,小明將上一節等待 ready 位的“超時”C語言程式碼:
修改為:
修改後的C語言程式碼的確更加簡潔了,但是好用嗎?我們編譯這段程式碼並執行:
怪,thread() 執行緒函式明明在 2 秒後就將 ready 置位了,怎麼還是輸出了 “time out”呢?小明對此困惑不已。
解析其實小明遇到的問題很像腦筋急轉彎,如果讀者和小明一樣感到困惑,一定是因為“沒反應過來”。cond_timeout() 函式沒有像小明的預期一樣工作的原因很簡單:cond 引數只是 cond_timeout() 函式被呼叫的時候的狀態,之後執行緒函式 thread() 無論如何修改 ready,也不會影響到 cont_timeout() 函數里的 cond。
那上面的“超時”C語言程式碼就不能封裝,想用時,就只能一行一行寫了?當然不是,將“超時”程式碼封裝為函式不合適,還可以封裝成宏:
上面這段C語言程式碼比較簡單,值得說明的一個小技巧是將 {} 放入 (),如此一來,整個 cond_timeout 宏就相當於一條語句,這是 Linux 核心中相當常用的宏定義方法。
cond_timeout 宏 __cond 條件成立,或者等待__cond 條件成立時間超過__timeout,都會到達 (!(__cond))這一行 此時:
顯然,如果這時 cond 成立了,cond_timeout 宏返回的就是 0 表示“等待 cond 條件沒有超時”,否則 cond_timeout 宏返回 1 表示“未能等到 cond 條件成立,超時了”。
現在再將 cond_timeout 寫入 main 函式,測試其是否可以正常工作,修改後的C語言程式碼如下,請看:
編譯並執行這段C語言程式碼,得到如下結果:
因為 thread() 函式 2 秒後將 ready 置位了,所以 cond_timeout 宏沒有返回超時。現在將 thread() 函數里的 sleep(2) 改為 sleep(6),相關C語言程式碼如下,請看:
編譯並執行修改後的C語言程式碼,得到如下結果:
一切與預期一致。
小結從本節可以看出,define 宏定義有時可以做到函式無法做到的事情。其實想想也能夠明白,define 宏定義只是將C語言程式碼暫時“打包”,如果宏被呼叫,編譯器就將包裹展開,這麼看來,define 宏定義實際上只是將若干行程式碼取了一個名字而已。我們使用 gcc -E 命令獲取編譯器預處理後的C語言程式碼:
# gcc -E t.c得到如下結果:
能夠看出,define 宏定義 cond_timeout 本身並沒有生成相關的預處理程式碼,反而在其被呼叫的地方,編譯器將宏的程式碼直接展開了,這一點和函式是不同的。