在c及c++語言中允許用乙個識別符號來表示乙個字串,稱為巨集,該字串可以是常數、表示式、格式串等。在編譯預處理時,對程式中所有出現的「巨集名」,都用巨集定義中的字串去代換,這稱為「巨集代換」或「巨集展開」。巨集定義是由源程式中的巨集定義命令完成的。巨集代換是由預處理程式自動完成的。若字串是表示式,我們稱之為函式式巨集定義,那函式式巨集定義與普通函式有什麼區別呢?我們以下面兩行**為例,展開描述:
函式式巨集定義:#define max(a,b) ((a)>(b)?(a):(b))
普通函式 : max(a,b)
(1)函式式巨集定義的引數沒有型別,預處理器只負責做形式上的替換,而不做引數型別檢查,所以傳參時要格外小心。
(2)呼叫真正函式的**和呼叫函式式巨集定義的**編譯生成的指令不同。
如果max是個普通函式,那麼它的函式體return a > b ? a : b; 要編譯生成指令,****現的每次呼叫也要編譯生成傳參指令和call指令。而如果max是個函式式巨集定義,這個巨集定義本身倒不必編譯生成指令,但是****現的每次呼叫編譯生成的指令都相當於乙個函式體,而不是簡單的幾條傳參指令和call指令。所以,使用函式式巨集定義編譯生成的目標檔案會比較大。
(3)函式式巨集定義要注意格式,尤其是括號。
如果上面的函式式巨集定義寫成 #define max(a, b) (a>b?a:b),省去內層括號,則巨集展開就成了k = (i&0x0f>j&0x0f?i&0x0f:j&0x0f),運算的優先順序就錯了。同樣道理,這個巨集定義的外層括號也是不能省的。若函式中是巨集替換為 ++max(a,b),則巨集展開就成了 ++(a)>(b)?(a):(b),運算優先順序也是錯了。
(4)若函式引數為表示式,則普通函式的呼叫與函式式巨集定義的替換過程是不一樣的。
普通函式呼叫時先求實參表示式的值再傳給形參,如果實參表示式有side effect,那麼這些sideeffect只發生一次。例如max(++a, ++b),如果max是普通函式,a和b只增加一次。但如果max函式式巨集定義,則要展開成k = ((++a)>(++b)?(++a):(++b)),a和b就不一定是增加一次還是兩次了。所以若引數是表示式,替換函式式巨集定義時一定要仔細看好。
(5)函式式巨集定義往往會導致較低的**執行效率。
看下面一段**:
int a=;若是普通函式,則通過遞迴,可取的最大值,時間複雜度為o(n)。但若是函式式巨集定義,則巨集展開為( a[n]>max(n-1)?a[n]:max(n-1) ),其中max(n-1)被呼叫了兩遍,這樣依此遞迴下去,時間複雜度會很高。int max(n)
int main()
儘管函式式巨集定義和普通函式相比有很多缺點,但只要小心使用還是會顯著提高**的執行效率,畢竟省去了分配和釋放棧幀、傳參、傳返回值等一系列工作,因此那些簡短並且被頻繁呼叫的函式經常用函式式巨集定義來代替實現。
C語言巨集定義和巨集定義函式
c語言巨集定義和巨集定義函式 巨集定義可以幫助我們防止出錯,提高 的可移植性和可讀性等。在軟體開發過程中,經常有一些常用或者通用的功能或者 段,這些功能既可以寫成函式,也可以封裝成為巨集定義。那麼究竟是用函式好,還是巨集定義好?這就要求我們對二者進行合理的取捨。我們來看乙個例子,比較兩個數或者表示式...
C 語言 巨集定義和巨集函式
在軟體開發過程中,經常有一些常用或者通用的功能或者 段,這些功能既可以寫成函式,也可以封裝成為巨集定義。那麼究竟是用函式好,還是巨集定義好?這就要求我們對二者進行合理的取捨。我們來看乙個例子,比較兩個數或者表示式大小,首先我們把它寫成巨集定義 define max a,b a b a b 其次,把它...
巨集定義函式
巨集定義也可以成為 巨集代換 c語言提供的三種預處理功能的其中一種,這三種預處理包括 巨集定義 檔案包含 條件編譯。巨集定義和操作符的區別是 巨集定義是替換,不做計算,也不做表示式求解。c語言編譯工具會在預處理階段,將巨集名替換為字串。所以,我們可以把巨集定義理解為,發生在程式編譯之前的字串替換操作...