extern "C" 雙重含義:被修飾目標具有 extern 特性(外部鏈接),且遵循 C 語言鏈接約定。
extern 關鍵字extern 聲明,擴展可見範圍。extern int A; printf("%d", A); A = 10;extern 聲明。鏈接器解析符號,避免重複定義錯誤。file1.c: int A = 5; file2.c: extern int A; printf("%d", A);extern(外部鏈接)。內部函數需加 static。調用處用 extern 聲明(通常放入頭文件)。鏈接時在各模塊搜索定義。"C" 鏈接約定問題背景:C++ 支持函數重載,編譯器對重載函數生成不同內部修飾名(Name Mangling),如 _eat_beef、_eat_fish。C 編譯器不修飾,foo 符號即 _foo。
若 C++ 直接調用已編譯的 C 庫函數,符號不匹配導致鏈接失敗。
extern "C"// 告訴 C++ 編譯器:foo 使用 C 鏈接,去庫中找 _foo 而非 _foo_int_int
extern "C" {
int foo(int x, int y);
}
標準庫頭文件已由廠商加上 extern "C",故可直接 #include 使用。
lib.c)// lib.h
#ifndef _LIB_H_
#define _LIB_H_
int add(int x, int y);
#endif
// lib.c
#include "lib.h"
int add(int x, int y) { return x + y; }
#include <iostream>
extern "C" {
#include "lib.h"
}
int main() {
std::cout << add(1, 2) << std::endl; // 正確鏈接到 _add
return 0;
}
若 C++ 直接 #include "lib.h" 無 extern "C",鏈接器報錯:unresolved external symbol "int __cdecl add(int,int)"。
__cplusplus 宏守護使同一頭文件同時兼容 C/C++ 編譯:
#ifndef _LIB_H_
#define _LIB_H_
#ifdef __cplusplus
extern "C" {
#endif
int add(int x, int y);
#ifdef __cplusplus
}
#endif
#endif
__cplusplus 未定義,展開為 extern int add(int, int);。__cplusplus 定義,展開為 extern "C" { extern int add(int, int); },指定 C 鏈接。Windows 用 C 編譯 DLL 導出函數。C 程序直接調用無問題;C++ 調用時,DLL 導出符號為 C 風格(_func),C++ 期望修飾名。在 DLL 頭文件用 extern "C" 聲明導出函數,即可讓 C++ 正確鏈接。許多第三方 DLL 頭文件均採用此模式。