LNK2005 是 Visual C++ 連結器報告的「符號重複定義」錯誤。本文記錄作者在學生資訊管理系統專案中遇到 LNK2005 的完整分析、定位與修復過程,揭示 #include 機制與編譯單元的本質關係。
stuheader.h // 結構體定義、函數宣告
stufun.c // 函數實作,#include "stuheader.h"
stuims.c // main(),錯誤地 #include "stufun.c" 而非 .h
錯誤訊息:
error LNK2005: _menu_select 已經在 stufun.obj 中定義
error LNK2005: _enter 已經在 stufun.obj 中定義
... (所有函數皆重複)
stufun.c 中函數無 static,預設 extern,產生全域符號#include "stufun.c" 將實作代碼直接文本插入 stuims.cstufun.c 編譯 → stufun.obj 含所有函數符號stuims.c 編譯(含展開的 stufun.c) → stuims.obj 又含所有函數符號// stuims.c
#include "stuheader.h" // 只包含宣告
// 移除 #include "stufun.c"
// 編譯命令
cl stufun.c stuims.c /FeStudentSystem.exe
// 或分別編譯再連結
cl /c stufun.c
cl /c stuims.c
link stufun.obj stuims.obj /OUT:StudentSystem.exe
原則:.c 檔不互相 include,只 include .h;多 .c 經獨立編譯再連結。
// stufun.c 所有函數加 static
static int menu_select() { ... }
static int enter(STUDENTS t[]) { ... }
// ...
// stuims.c 仍可 #include "stufun.c"
// 但每個包含該 .c 的編譯單元都會產生獨立副本,浪費空間
StudentSystem/
├── stuheader.h // 介面:結構體、常數、函數宣告 (extern 隱含)
├── stufun.c // 實作:#include "stuheader.h",函數定義無 static
├── stuims.c // 入口:#include "stuheader.h",呼叫介面函數
└── Makefile / .sln // 建置腳本
| 成因 | 現象 | 解決 |
|---|---|---|
| 頭檔定義變數/函數實作 | 多 .c include 同頭檔 → 重複 | 頭檔只放宣告,定義移至 .c |
| 全域變數未加 extern | 頭檔 int g_val; 多處 include | 頭檔 extern int g_val;,.c 定義 int g_val; |
| 第三方庫符號衝突 | 引入兩個庫定義同符號 | 命名空間隔離、靜態連結單一庫 |
| 模板/內聯函數在 .c 定義 | 隱含內聯仍可能產生實體 | 移至頭檔或加 static inline |