標準 C/C++ 頭檔頂部常見:
#ifdef __cplusplus
extern "C" {
#endif
// C 介面聲明...
#ifdef __cplusplus
}
#endif
這段「圍牆」究竟在幹什麼?本文從**名稱修飾**、**連結模型**、**混合編程場景**三維度,徹底講透 extern "C" 的核心機制與慣用法。
void foo(int, float); → _Z3fooif (GCC) / ?foo@@YAXHM@Z (MSVC)。_foo 或 foo(平台相關)。#include "c_header.h" 並呼叫 C 函數,連結器會尋找修飾名,**找不到 C 目標檔中的原始符號 → 連結失敗**。extern "C" 雙重語義extern)。本質:extern "C" 是**連結規範**,不影響語義分析(型別檢查仍按 C++ 規則)。
// c_header.h (純 C)
#ifndef C_HEADER_H
#define C_HEADER_H
int add(int a, int b); // 僅 extern,無 extern "C"
#endif
// c_impl.c
#include "c_header.h"
int add(int a, int b) { return a + b; }
// cpp_main.cpp
extern "C" { // 關鍵:包裹 C 頭檔
#include "c_header.h"
}
int main() { add(2,3); }
// cpp_header.h
#ifndef CPP_HEADER_H
#define CPP_HEADER_H
#ifdef __cplusplus
extern "C" { // C++ 端聲明為 C 連結
#endif
int add(int a, int b);
#ifdef __cplusplus
}
#endif
#endif
// cpp_impl.cpp
#include "cpp_header.h"
int add(int a, int b) { return a + b; }
// c_main.c
#include "cpp_header.h" // 錯誤!C 編譯器不識別 extern "C"
// 正確:c_main.c 另寫聲明
extern int add(int a, int b); // 純 C 外部聲明
int main() { add(2,3); }
extern "C" { #include "clib.h" }。
// 內部實現檔,不想導出符號
static void internal_helper() { ... } // 內部連結,更優
// 或
namespace { void internal_helper() { ... } } // C++ 匿名命名空間
static / 匿名命名空間優於 extern "C" 隔離內部符號。
| 誤區 | 正確認知 |
|---|---|
extern "C" 讓 C++ 代碼變成 C 代碼 | 僅改變**連結名稱**,語法、型別檢查、物件模型仍是 C++ |
C 代碼裡寫 extern "C" | C 編譯器報語法錯誤,C 不支援此關鍵字 |
類成員函數加 extern "C" | 非法:成員函數有 this 指標,無 C 對應物 |
模板函數加 extern "C" | 非法:模板實例化產生多個符號,無法對應單一 C 符號 |
extern "C" 影響類佈局 | 不影響,類佈局由 C++ ABI 決定 |
extern 聲明,不寫 extern "C"。extern "C" { #include "c.h" }。#ifdef __cplusplus extern "C" { #endif ... #ifdef __cplusplus } #endif 包裹。#include "cpp_c_iface.h"(頭檔已處理 extern "C"),或手寫 extern int foo();。gcc),C++ 檔用 C++ 編譯器(g++),目標檔統一連結器(g++ 驅動連結)處理。extern "C" 是 C++ 與 C、甚至其他語言(Fortran, Assembly, Rust)互操作的**連結層適配器**。它的唯一使命:**關閉名稱修飾,生成 C 風格符號**。理解名稱修飾原理、連結模型差異、四大混合場景慣用法,是大型跨語言專案架構師的必修功課。