為何 GlobalVariables.h 中僅有 extern MusicSettingStruct *Music; (聲明),卻能在 function.c 直接使用,且編譯鏈接通過?
| 概念 | 關鍵字 | 作用 | 記憶體分配 | 可出現次數 |
|---|---|---|---|---|
| 聲明 | extern | 告訴編譯器:「這個符號存在,類型是這個,位址稍後鏈接器解析」 | 不分配 | 多次 (多檔案 #include) |
| 定義 | 無 extern (或帶初始化) | 實際創建變數,分配記憶體 | 分配 | 全程僅一次 (一個 .c 檔) |
global_vars.h —— 僅聲明
#ifndef GLOBAL_VARS_H
#define GLOBAL_VARS_H
// 類型定義
typedef unsigned short UINT16;
typedef struct {
int volume;
int track;
} MusicSettingStruct;
// 變數聲明 (關鍵:extern)
extern MusicSettingStruct *g_pMusic; // 建議加 g_ 前綴區分全域
// 函數聲明
void music_init(void);
void music_play(int track);
#endif
global_vars.c —— 唯一定義 (必須存在!)
#include "global_vars.h"
#include <stdlib.h>
// 定義:分配記憶體,初始化
MusicSettingStruct *g_pMusic = NULL; // 定義在此,無 extern
void music_init(void) {
g_pMusic = malloc(sizeof(MusicSettingStruct));
if (g_pMusic) {
g_pMusic->volume = 50;
g_pMusic->track = 1;
}
}
function.c —— 包含頭檔直接用
#include "global_vars.h"
#include <stdio.h>
void function_play_current(void) {
if (g_pMusic) { // 直接使用,編譯器信任聲明
printf("Playing track %d at vol %d\n", g_pMusic->track, g_pMusic->volume);
}
}
main.c、init.c) 含有 MusicSettingStruct *Music = ...; (無 extern) 的定義。鏈接器在合併所有 .o 檔時找到了定義,解析成功。int x; (無初始化、無 extern),鏈接器會將其合併為一個定義。但 C99/C11 標準不保證此行為,且帶初始化 int x = 1; 則報重複定義錯誤。請勿依賴此特性。Music 定義在預編譯的 .lib/.a 中,鏈接時自動解析。
// main.c
#include "global_vars.h" // 只有 extern 聲明
int main() { g_pMusic->volume = 10; return 0; }
// global_vars.c 不存在 或 未加入專案建置
// 編譯 main.c 通過,鏈接失敗:
// undefined reference to `g_pMusic'
extern 聲明,嚴禁定義 (除非 static 或 inline 函數)。static 限制檔案內部可見性:static int internal_var; 避免汙染全域命名空間。static 變數 + 介面函數 get/set,降低耦合,利於測試與維護。