將 int main(void) 改為 static int main(void) 會導致連結器錯誤:
/usr/lib/gcc/.../crt1.o: In function `_start':
(.text+0x18): undefined reference to `main'
collect2: ld returned 1 exit status
程式啟動時,執行的是啟動例程 _start(位於 crt1.o),它會呼叫 __libc_start_main,後者再呼叫使用者定義的 main。crt1.o 中宣告 main 為 外部符號(未定義,需連結時解析)。
static 修飾函數時,賦予內部連結屬性,該符號僅在本編譯單元可見,不會輸出到目標檔的全域符號表。連結器掃描目標檔的 .rel.text 區段進行符號解析時,忽略內部符號,因此找不到 main 的定義。
$ gcc -c static_main.c # 成功產生 .o
$ gcc static_main.c # 連結失敗
hello.c
│
├─ gcc -E (預處理) → hello.i
│
├─ gcc -S (編譯) → hello.s (cc1 編譯器)
│
├─ gcc -c (組譯) → hello.o (as 組譯器)
│
└─ gcc (連結) → a.out (collect2 → ld 連結器)
hello.o + crt1.o + crti.o + crtbegin.o + libc + crtend.o + crtn.o
crt1.o:包含 _start,引用未定義符號 main(nm 可見 U main)。collect2:GCC 封裝的連結器前端,最終呼叫 GNU ld。-lc:連結 C 標準庫 libc.a/.so。| 宣告 | 連結屬性 | 符號表類型 | 連結器是否解析 |
|---|---|---|---|
int main() | 外部連結 | 全域 (T) | 是 |
static int main() | 內部連結 | 區域 (t) | 否 |
main 雖非關鍵字,但被 C 執行環境約定為使用者進入點符號。static 限制符號可見範圍,破壞啟動代碼對 main 的外部引用。