Preprocessor


我們以下用 cpp (C-preprocessor) 來代表預處理器。
cpp 幫我們做了三件重要的事情

  • 加入 header file 或說 "複製貼上" #include "file"
  • 去註解 (這就不花篇幅講解了)
  • macro 替代

cpp 的語法很多,用途也很廣泛,尤其在跨平台或是想減少重複 code 時有很大的功用,
cpp 的語法就不多介紹,在此主要介紹cpp做的事情。

macro


我們可能常常見到形如以下的 code

#define M_VAL 100
#define TEST
int v_val = 200;

void function(void)
{

#ifdef TEST
    int a = M_VAL;
    int b = v_val;
    printf("test\n");
#else
    printf("hello\n");
#endif

}

而經過 cpp 後大略會長這樣

int v_val = 200;

void function(void)
{
    int a = 100;
    int b = v_val;
    printf("test\n");
}

可以注意到 a, b 變數。姑且不論最終 compiler 幫我們做的最佳化,至少在此可以看得出來 macro 只是單純去 "替換", 理論上效率應會高於 b 的方式。但是 b 仍舊有它的好處,因為 macro替換的動作並無"型態"的概念。

事實上這樣的比較並不公平,因為 v_val 是一個非常數變數,一個可能的實作是會從 memory 放到暫存器後 assign。這邊只是要說明cpp做的僅是替換。

Header File


通常我們會以 h 當作header的副檔名。在某些情況下也有人 include source file。

假設以下我們有兩個檔案 f.h 以及 f.c

f.h

//f.h

typedef struct node_t
{
    int data;
    struct node_t * next;
} node_t;

f.c

//f.c
#include "f.h"

而當 cpp f.c 結束後 我們會看到的是

typedef struct node_t
{
    int data;
    struct node_t * next;
} node_t;

也就是說,我們可以把 #include "file.h" 理解成一個複製貼上的動作,
將 file.h 的內容貼到 #include "file.h" 的地方。

--

include guard

延續上面的說明,我們考慮一個狀況,若一開始在 f.c 內是這樣寫

#include "f.h"
#include "f.h"

那這樣 node_t 就會重複宣告。 要避免這個問題,我們通常使用 "include guard" (應該說,就算沒有碰到 double inclusion,在 header 中大家還是會加入 include guard)

以下是個範例 f.h

#ifndef F_H
#define F_H

typedef struct node_t
{
    int data;
    struct node_t * next;
} node_t;

#endif

若加上 include guard後,在第一次 include 後 F_H 會被定義。 而第二次的 include 因為 F_H 曾被定義,因此等價於忽略此檔案。

以下是說明

#ifndef F_H    // F_H 沒被定義過,因此有下面的 code
#define F_H   // 第一個include,在此 F_H 被定義

typedef struct node_t
{
    int data;
    struct node_t * next;
} node_t;

#endif

#ifndef F_H  // 已經被定義過,因此忽略到 #endif
#define F_H

typedef struct node_t
{
    int data;
    struct node_t * next;
} node_t;

#endif

results matching ""

    No results matching ""