函式式巨集定義與普通函式

2021-06-23 06:05:16 字數 1390 閱讀 1607

函式式巨集定義與普通函式

在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=;

int max(n)

int main()

若是普通函式,則通過遞迴,可取的最大值,時間複雜度為o(n)。但若是函式式巨集定義,則巨集展開為( a[n]>max(n-1)?a[n]:max(n-1) ),其中max(n-1)被呼叫了兩遍,這樣依此遞迴下去,時間複雜度會很高。

儘管函式式巨集定義和普通函式相比有很多缺點,但只要小心使用還是會顯著提高**的執行效率,畢竟省去了分配和釋放棧幀、傳參、傳返回值等一系列工作,因此那些簡短並且被頻繁呼叫的函式經常用函式式巨集定義來代替實現

函式式巨集定義與普通函式

在 及c 語言中允許用乙個識別符號來表示乙個字串,稱為巨集,該字串可以是常數 表示式 格式串等。在編譯預處理時,對程式中所有出現的 巨集名 都用巨集定義中的字串去代換,這稱為 巨集代換 或 巨集展開 巨集定義是由源程式中的巨集定義命令完成的。巨集代換是由預處理程式自動完成的。若字串是表示式,我們稱之...

函式式巨集定義與普通函式

在 及c 語言中允許用乙個識別符號來表示乙個字串,稱為巨集,該字串可以是常數 表示式 格式串等。在編譯預處理時,對程式中所有出現的 巨集名 都用巨集定義中的字串去代換,這稱為 巨集代換 或 巨集展開 巨集定義是由源程式中的巨集定義命令完成的。巨集代換是由預處理程式自動完成的。若字串是表示式,我們稱之...

函式式巨集定義與普通函式

在 及c 語言中允許用乙個識別符號來表示乙個字串,稱為巨集,該字串可以是常數 表示式 格式串等。在編譯預處理時,對程式中所有出現的 巨集名 都用巨集定義中的字串去代換,這稱為 巨集代換 或 巨集展開 巨集定義是由源程式中的巨集定義命令完成的。巨集代換是由預處理程式自動完成的。若字串是表示式,我們稱之...