記憶體與指標

記憶體區段錯誤

/ seg-fault /

每個 C 程式設計者遲早都會遇到「Segmentation fault」這幾個字,以及一個就這麼死掉的程式。這是作業系統在說「你試圖碰你不被允許碰的記憶體」。硬體察覺了一次無效的記憶體存取並予以拒絕,作業系統殺掉了這個行程,而不是任由它胡來。

更精確地說,記憶體管理硬體會對照「你的行程被允許做什麼」來檢查每一次存取。當你讀或寫一個沒有對映進你定址空間的位址,或寫入唯讀記憶體時,硬體會引發一個錯誤(fault);核心把它轉成信號 SIGSEGV,其預設動作是終止行程(常會傾印核心以供日後除錯)。常見的觸發原因都歸結為一個壞位址:解參考空指標、使用懸置或野指標、跑出陣列或緩衝區的結尾、用失控的遞迴把堆疊撐爆,或試圖修改唯讀記憶體中的字串字面值。

這裡是關鍵又誠實的部分:記憶體區段錯誤是症狀,不是臭蟲。它告訴你「在這裡發生了一次無效存取」,但真正的瑕疵通常更早——指標在上游某處變壞了,或一次越界寫入早在當掉之前就毀了東西。更糟的是,未定義行為根本不保證會當掉:同一個壞指標在不同的執行或建構下,可能悄悄毀損記憶體而從不 segfault。所以把 segfault 當成起點線索,動用除錯器或消毒器去找出位址究竟在哪裡出了錯,並且絕不要以為「沒有 segfault」就代表「沒有臭蟲」。

int *p = NULL; *p = 5; 幾乎一定會 segfault,因為位址 0 被刻意設為沒有對映。但 int a[3]; a[1000000] = 5; 可能 segfault,也可能悄悄毀掉別處的某段記憶體——未定義行為,不保證會當掉。

空指標解參考可靠地觸發錯誤;越界寫入可能悄悄毀損。

segfault 是壞存取的幸運結局——它當場停下程式。同一個未定義行為的不幸結局是悄悄毀損、不當掉,在很遠處才浮現。用除錯器或 AddressSanitizer 找出根本原因(指標在哪裡變壞);出錯的地點很少就是臭蟲本身。

又称
segfaultSIGSEGV記憶體區段錯誤區段錯誤