#include 是每位 C/C++ 開發者每天使用的指令,但其背後的預處理機制、編譯單元形成、單一定義規則(ODR)往往被忽視。本文從原理層面完整梳理。
#include 由**預處理器**處理,單純將指定檔案內容**原文展開**至當前位置。.h,任何文字檔(.c、.cpp、.txt、.abc)皆可包含。#include <file> → 系統包含目錄;#include "file" → 當前目錄 → 系統目錄。| 技術 | 語法 | 優點 | 缺點 |
|---|---|---|---|
| 宏衛兵 | #ifndef _UNIQUE_NAME_H | 標準 C/C++、跨編譯器、複製檔案仍生效 | 需手動保證宏名唯一,易衝突 |
#pragma once | 檔案首行 #pragma once | 簡潔、無需命名、編譯器保證物理檔案只編譯一次 | 非標準(主流支援)、複製檔案會失效(視為不同檔案) |
建議:專案統一使用宏衛兵(命名規範:PROJECT_MODULE_FILE_H),或雙重保險兩者並用。
int a;、extern int b;、void foo();)。不配置儲存空間(除非同時為定義)。extern 變數無初始化」、「類靜態成員宣告」、「typedef」、「using」外,**宣告即定義**。定義會配置/保留儲存空間。.c/.cpp)經預處理展開所有 #include 後形成的完整輸入。編譯器獨立編譯每個編譯單元產出 .obj,連結器合併。LNK2005 或未定義行為。// example_a.h
void func();
// example_a.cpp
#include "example_a.h"
void func() { /* 定義 */ }
多編譯單元 #include "example_a.h",連結時僅有一個 func 定義 → 符合 ODR。
// example_b.def
void func1(); void func2();
// example_b_1.cpp
void func1() {}
// example_b_2.cpp
#include "example_b.def"
#include "example_b_1.cpp" // 直接包含 .cpp!僅此處包含,不加入專案建置
void func2() {}
技巧:將實作檔 #include 到單一編譯單元,避免符號衝突,適用於模板顯式實例化、內聯函數集中編譯。
<iostream.h>、<string.h> → 全域命名空間。<iostream>、<cstring> → std:: 命名空間。using namespace std; 或 using std::cout; 使符號可見。.cpp(模板、內聯函數除外)。class Foo;)能減少 #include,加快編譯。<>,專案頭檔用 ""。