C 執行期啟動碼(crt0)
/ C-R-T-zero /
初學者常以為執行 C 程式是從 main 開始的,以為 main 是最先執行的東西。這並不全然正確。在你的 main 函式執行之前,系統提供的一小段程式碼早已在搭設舞台,而最終呼叫 main 的正是那段隱藏的程式碼,不是載入器,也不是你。這段啟動程式碼傳統上叫 crt0(C run-time, object 0),它被悄悄地連結進每個普通的 C 程式。
概括地說發生了什麼。載入器把程式映射進記憶體,跳到的不是 main,而是程式真正的進入點,一個常叫 _start 的標籤,它位於啟動程式碼之內。那段程式碼做 main 視為理所當然的雜務:它確保堆疊正確佈置好、收集核心交來的命令列引數與環境並把它們安排成 argc、argv 與環境、執行任何必須在 main 之前發生的初始化(例如某些全域物件的建構式),然後才帶著那些引數呼叫 main。當 main 回傳它的 int 值時,啟動程式碼並非就此停下:它取走那個回傳值、執行清理常式,並呼叫 exit,使那個值成為回報給命令列的程式結束狀態。
明白這點能釐清好幾件事。它解釋了為何 main 只要回傳一個數字、程式就「告訴命令列」那個數字:啟動程式碼替你把它帶到了 exit。它解釋了為何記在可執行檔裡的真正進入點是 _start 而非 main。而這也是為什麼在沒有 crt0 的不尋常獨立式或嵌入式環境中,你必須自己提供啟動碼,main 本身真的不夠。你通常從不撰寫、甚至從不看到這段程式碼;工具鏈會自動為你的平台連結正確的 crt0。
// 你所寫的: 執行期實際發生的: int main(void) { _start: // 真正的進入點(crt0) return 0; 佈置堆疊、argc、argv、環境 } call main() // <- main 在此執行 exit( 回傳值 ) // <- 成為結束狀態
main 不是最先執行的程式碼;啟動碼先佈置好引數,然後才呼叫它。
main 不是程式真正的進入點:連結器記錄的是 _start(在 crt0 內),它佈置好 argc/argv 並呼叫 main,再把 main 的回傳值轉成行程的結束狀態。