Contents
  1. 1. Header files
  2. 2. MACRO
    1. 2.1. GCC predefined macro
  3. 3. GCC preprocessor related options
  4. 4. Reference

C語言的前置處理(CPP:C Preprocessor)功能強大,了解其使用方法及時機能加快理解他人程式碼的速度與撰碼功力,本篇是我學習的筆記。

Header files

[header file section]
標頭檔內含函數的宣告和macro的定義,能讓眾多source file去引用。

剛學C的時候,我一直對標頭檔有很深的疑惑:為何不要把函數的宣告和實作寫在同一個.c檔中呢?何必費心的額外準備一個標頭檔再引入呢?

原來標頭檔有兩個主要目的:

  1. 作業系統會實作一系列系統呼叫(system call)並把相關的界面(interface)紀錄在標頭檔,我們可以透過引用標頭檔的方式使用系統呼叫或其他libries。
  2. 開發專案時,能透過標頭檔整理,集結相關的宣告與macro,並讓有需要相關功能的source files去引用,#include xxx相當於把該標頭檔複製到source files,如果沒有標頭檔則需要在每個原始碼中手動新增修改,增加維護的難度。

MACRO

macro是一個名字(identifier)和內容(content)的對應,#define xxx ooo指示CPP,要把文件中所有的xxx轉換成ooo;macro又區分為兩類:

  • Object-like macro: 看起來像普通物件定義,例如 #define BUFFER_SIZE 1024
  • function-like macro: 和object-like macro一樣,只是後面需要加上()並且可以餵食參數。

    • ()前不可有空格會轉換錯誤

      1
      2
      #define lang_init ()    c_init()
      lang_init() ==> () c_init()()
    • function-like macro定義好之後,只有在名字後面連接()才會被展開,例如:

      1
      2
      3
      4
      5
      extern void foo(void);
      #define foo() /* optimized inline version */
      ...
      foo();
      funcptr = foo;

      foo()會展開成macro的定義,而funcptr則會拿到void foo的位址。

GCC predefined macro

GCC預先定義好一些object-like macro,可以直接取用,在debug或其他用途上都很方便。

  • __FILE__
  • __LINE__
    兩者常被組合在一起用來產生錯誤訊息:
    1
    2
    3
    4
    fprintf (stderr, "Internal error: "
    "negative string length "
    "%d at %s, line %d.",
    length, __FILE__, __LINE__);

建構一個程式包含前處理(preprocessing),編譯(compilation),轉換成組合語言(assemlby)和連結(linking),這裡列出一些在前處理步驟有趣的options。

  • -E:令gcc在完成前處理階段後就停止,並把前處理後的結果輸出到STDOUT;輸入中不須前處理的檔案會被忽略掉。

  • -save-temps : 把建構過程產生的中介檔案(intermediate files)存下來,例如gcc -c -save-temps foo.c會在當前資料夾下產生

    • foo.i: 前處理後的結果
    • foo.s: 轉換成組合語言的結果
    • foo.o: 連結後的完整結果

Reference

Contents
  1. 1. Header files
  2. 2. MACRO
    1. 2.1. GCC predefined macro
  3. 3. GCC preprocessor related options
  4. 4. Reference