預處理器處理所有以 # 開頭的指令,在編譯器看到源碼前完成:文件包含、宏展開、條件編譯。輸出 .i 文件(展開後的純 C/C++ 代碼),隨後交給編譯器。
#include
#include // 系統頭文件:搜索標準包含路徑
#include "myhead.h" // 用戶頭文件:先搜索當前目錄,再搜索標準路徑
本質:將頭文件內容原文插入到 #include 位置。支持嵌套包含。
#define
#define PI 3.1415926
#define BUFFER_SIZE 1024
#define DEBUG_PRINT(fmt, ...) printf("[DEBUG] " fmt "\n", ##__VA_ARGS__)
#undef。
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define SQUARE(x) ((x)*(x))
黃金法則:
(a) (b) 防止運算符優先級錯亂。((a) > (b) ? (a) : (b)) 防止外部上下文優先級干擾。#define MAX(a,b) 而非 #define MAX (a,b)(後者被視為無參宏)。inline 函數| 特性 | 宏 | 內聯函數 | 普通函數 |
|---|---|---|---|
| 類型安全 | 無(文本替換) | 有 | 有 |
| 副作用風險 | 高(多次求值參數) | 無 | 無 |
| 調試可見性 | 不可見(展開後無行號) | 可見 | 可見 |
| 代碼膨脹 | 可能 | 可能 | 無(調用開銷) |
| 適用場景 | 常量、簡單表達式、編譯期計算 | 小函數、性能關鍵路徑 | 通用邏輯 |
⚠️ 宏參數多次求值陷阱:
SQUARE(i++)展開為((i++)*(i++))→i增加兩次!inline函數無此問題。
# 與 連接 ##
#define STR(x) #x // 字符串化:STR(hello) → "hello"
#define CONCAT(a,b) a##b // 連接:CONCAT(var, 1) → var1
#define MAKE_STR(a,b) #a #b // 組合:MAKE_STR(12, 34) → "12" "34" → "1234"
__LINE__:當前行號(整數)__FILE__:當前文件名(字符串)__DATE__、__TIME__:編譯日期/時間__STDC__:標準 C 模式為 1__cplusplus:C++ 編譯器定義(如 201703L 表示 C++17)#ifdef / #ifndef / #endif
#ifdef DEBUG
log("debug info");
#else
// 發布版無日誌開銷
#endif
#ifndef HEADER_GUARD
#define HEADER_GUARD
// 頭文件內容
#endif
#if / #elif / #else / #endif
#if VERSION >= 2
new_api();
#elif VERSION == 1
legacy_api();
#else
#error "Unsupported version"
#endif
條件必須是整數常量表達式(可包含 defined(MACRO))。
#ifdef _WIN32
#define DLLEXPORT __declspec(dllexport)
#include
#elif defined(__linux__)
#define DLLEXPORT __attribute__((visibility("default")))
#include
#else
#error "Unsupported platform"
#endif
#error 與 #pragma#error "message":強制編譯失敗,輸出錯誤信息。用於檢測不支持的配置。#pragma once:非標準但廣泛支持的頭文件防衛替代方案。#pragma pack(n):控制結構體對齊(慎用)。| 陷阱 | 錯誤代碼 | 正確寫法 | 原因 |
|---|---|---|---|
| 宏尾分號 | #define N 100; | #define N 100 | 展開後 int a = 100;; |
| 參數無括號 | #define SQR(x) x*x | #define SQR(x) ((x)*(x)) | SQR(a+1) → a+1*a+1 |
| 宏名與括號有空格 | #define MAX (a,b) ... | #define MAX(a,b) ... | 被視為無參宏,展開混亂 |
| 多次求值副作用 | MAX(i++, j++) | 用 inline 函數 | 參數可能被計算多次 |
| 字符串內宏不展開 | printf("VERSION"); | printf("%s", STR(VERSION)); | 引號內視為普通字符 |
constexpr/const,宏僅用於編譯期計算、條件編譯、字符串化。inline / constexpr 函數。#pragma once)。MAX_BUFFER_SIZE),避免與變量衝突。#if 而非 #ifdef,可檢測宏定義為 0 的情況。預處理器是 C/C++ 獨特的「編譯期元編程」工具。掌握其文本替換本質、作用域規則、參數展開陷阱,配合現代 C++(constexpr、inline、模板)替代傳統宏用法,能寫出更安全、可維護、跨平台的代碼。