errno 與 perror/strerror(errno and perror/strerror)
想像你請一位櫃台人員去取一份文件,他空手回來只說了一句「不行」。這不太有用——你想知道為什麼:是文件不見了、你沒有權限看、還是櫃子鎖著?許多 C 函式回報失敗時只給一句乾巴巴的「不行」(常是回傳負一),而「為什麼」則被留在另一個你事後可以查的地方。那個地方就是 errno 變數。
就機制而言,errno 是 C 標準函式庫公開的一個特殊整數(它的行為像全域變數,但實際上是每執行緒各有一份,這樣執行緒之間才不會互相覆蓋彼此的錯誤碼)。當一個系統呼叫包裝或函式庫函式失敗時,它會回傳一個哨兵值來表示失敗——通常是負一,或對回傳指標的函式而言是 NULL——並把一個描述具體原因的數值錯誤碼存進 errno。每個碼都有名字,例如 ENOENT(沒有這個檔案或目錄)、EACCES(權限被拒)、EINTR(被中斷)。要把數字轉成可讀文字,標準函式庫提供 strerror(),它為某錯誤碼回傳一段給人看的字串;以及 perror(),它把你的訊息印出來、後面接著目前 errno 對應的文字,送到標準錯誤。正確的做法是先檢查回傳值,之後才讀 errno——因為一次成功的呼叫並不會把 errno 重設為零。
為何重要:這是 C 解釋一個操作為何失敗的主要方式,而正確使用它,就是「它當掉了」與「open 失敗:沒有這個檔案或目錄」之間的差別。關鍵的紀律是順序:errno 只有在一次明確表示失敗的呼叫之後立刻去讀才有意義。沒先確認呼叫真的失敗就讀 errno,是個經典的錯誤,因為函式並不被要求在成功時清除 errno,所以你可能讀到某個更早的呼叫留下的過時錯誤碼。
FILE *f = fopen("missing.txt", "r"); if (f == NULL) perror("fopen"); ——這會印出類似:fopen: No such file or directory。perror 把你的標籤「fopen」與 strerror 對目前 errno 裡 ENOENT 碼所給的文字組合起來。
先檢查回傳值,再把 errno 的錯誤碼轉成可讀的訊息。
errno 只有在一次回報失敗的呼叫之後緊接著才有效。成功的呼叫並不保證讓 errno 維持為零,所以絕不要單憑 errno 來判斷某事是否失敗——要測回傳值,並且只在想知道「為什麼」時才讀 errno。