【C】【Compiler】Inline、Function、Macro 秒懂神解釋

曾經學過C語言,
矇矇懂懂只知道 function跟 macro是類似的東西,不清楚具體的差異為何。

將以 C語言 來舉例,
通常在C語言寫程式中,第一行都是
#include <stdio.h>



井字符號 是給編譯器讀的(之後交由預處理器來處理),指示編譯器引入 stdio.h。
The preprocessor replaces the line #include <stdio.h> with the textual content of the file 'stdio.h', which declares the printf() function among other things.
雖語言相同、但因編譯器不同,
所以在 VSCode中寫C++時,會要求 #include stdafx.h,
即使最後輸出的結果是一樣的。

預處理器 preprocessor

在編譯前,編譯器就先做好的事前準備,諸如#include、#define等等。
所有的預處理器 preprocessor 的命令都是由 # 開頭。
C語言預處理的指令12個:
  1. #include
  2. #define
  3. #ifdef
  4. #ifndef
  5. #undef
  6. #error
  7. #line
  8. #if
  9. #else
  10. #elif
  11. #endif
  12. #pragma
而在 Objective-C 多個一個 #import

編譯指示 directive (pragma)

上述第12項 #pragma 開頭的指示,
同樣是給編譯器看,但後面接著的參數命令有些編譯器看不懂
所以這一項指示是隨著編譯器不同,所產生的結果也可能會有所不同。








macro 巨集

在滑鼠、鍵盤裡面常聽到巨集的字眼,
具體來說是 當你按下此按鍵,他會自動幫你做一連串 預先設定好的動作或按鍵。
譬如說,我自訂了一個功能鍵"施放絕招",按下之後鍵盤會在很短的時間內 向電腦發出 ↑ + ↑ + ↓ + ↓ + ← + ← + → + → + A + B + A + B  的連續按鍵。不然每次絕招都按到手殘廢了、絕招還不一定施展得出來。

macro簡言之,就是進行代換,將複雜的東西代換成簡單的。

#define PI 3.14159

之後在主程式看到PI這個單字,就會將PI以3.14159取代。

在C語言中 macro進行了純文字的代換(textual replacement),概念就是大家都來一份

而這裡也有一個很大的坑

# define Square(x) x*x

若在主程式中,下述能得到 25,看起來沒有問題
printf("\n %d", square(5));
但如果是以下,卻會得到 11
printf("\n %d", square(3+2));

因為純文字替換後相當於這樣:
printf("\n %d", 3+2 * 3+2);


解決辦法為下,多了看起來有點冗餘的括號:
# define Square(x) ((x)*(x))
優點:大家都有一份,要用馬上就有得用,快速
缺點:大家都要一份,頗占空間,產出檔案較大
備註:定義macro後,交給預處理器來處理,發生在編譯之前。








function 函式


『咦,這裡有出現這一段、那裡也有出現這一段,出現的程式碼一模一樣。
啊,那我乾脆把這一段功能 寫成function好了,讓程式碼簡潔又易懂。』

就是一般寫在程式裡面的 function,概念就是同樣的空間,大家一起用
給定輸入,使用同樣的邏輯,輸出返回結果(不一定要回傳結果)。

優點:節省空間呀,大家一起共用空間,產出的檔案較小
缺點:因為function初始化時CPU要進行 Stack push、pop的動作,缺點就是慢了些








inline function 行內(內嵌、內聯)函式

inline 意思是把函式展開(function expand)。
但 "函式展開" 這句話過於抽象,
直觀解釋就是:像macro那樣 把function的程式碼 直接塞進程式碼行內,大家都來一份function

使用時機:當function體積小、卻被頻繁使用時

優點:快速,大家都有一份,要用馬上就有得用,不用等push、pop
缺點:大家都要一份,很占空間,產出檔案較大。大量重複程式碼甚至會降低cpu快取命中率
備註:發生在進行編譯的時候。不同的編譯器會根據function使用到的次數、大小,來做不同的判斷、優化。
另外被內聯,等於你寫好的函式灰飛煙滅了、找不到函式的所在位置,除非前面有加上static、或是寫第二個相同名稱的function 前面不加inline。




noinline 不要行內函式

有強制內聯方法,也有強制不要內聯的方法。
在function不想被內聯的時候使用。