glibc 函式通常回傳特殊值(-1、NULL、EOF)表示失敗,但無法得知具體錯誤原因。真正的錯誤資訊儲存在全域變數 errno(定義於 <errno.h>,型別 volatile int)。
errno 取得錯誤碼。| 宏名 | 含義 | 典型場景 |
|---|---|---|
EDOM | 定義域錯誤 | 數學函式參數超出定義域(如 sqrt(-1)) |
ERANGE | 值域錯誤 | 結果超出可表示範圍(如 exp(1000)) |
EAGAIN/EWOULDBLOCK | 資源暫時不可用 | 非阻塞 IO 無資料、鎖被佔用 |
EFAULT | 無效指標 | 傳遞野指標給系統呼叫(幾乎所有系統通用) |
EAGAIN與EWOULDBLOCK數值相同,switch判斷需謹慎,唯一可信來源是文件。
#include <string.h> char *strerror(int errnum); // 非執行緒安全,回傳靜態緩衝區 char *strerror_r(int errnum, char *buf, size_t n); // 執行緒安全,寫入使用者緩衝區
strerror:MT-Unsafe race:strerror,多執行緒環境下資料競爭。strerror_r:MT-Safe,但回傳值與 buf 皆可能指向靜態區,需注意 GNU 版本差異(XSI vs GNU)。#include <stdio.h> void perror(const char *message);
message == NULL:僅列印錯誤描述(如 "No such file or directory")。message != NULL:列印 "message: 錯誤描述" 格式。errno 被後續函式覆蓋。char *program_invocation_name; // 等同 argv[0] 完整路徑 char *program_invocation_short_name; // 僅程式名稱(無目錄)
需定義 _GNU_SOURCE 並連結 glibc,非 GNU 系統無效。
void error(int status, int errnum, const char *format, ...); void error_at_line(int status, int errnum, const char *fname, unsigned int lineno, const char *format, ...);
status == 0:格式化列印錯誤,error_message_count++,繼續執行。status != 0:列印後呼叫 exit(status) 終止程式。程式名: 格式化字串: errno描述(error_at_line 額外含檔名:行號)。error_print_progname 可自訂程式名輸出邏輯;error_one_per_line 非零時每行僅列印一錯誤。<err.h>,GNU 系統不推薦)warn(), vwarn(), warnx(), vwarnx() err(), verr(), errx(), verrx()
功能類似 error() 族但介面不同,移植性考量下優先使用 GNU 版本。
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <error.h>
void read_config(const char *path) {
FILE *fp = fopen(path, "r");
if (!fp) {
error(EXIT_FAILURE, errno, "無法開啟設定檔 '%s'", path);
}
// ...
}
glibc 提供層次豐富的錯誤報告機制:errno 為核心,strerror_r 供執行緒安全取字串,perror 供快速列印,error()/error_at_line() 為格式化報告首選。遵循「先查回傳值、再讀 errno、最後查文件」流程,配合 _GNU_SOURCE 使用 error() 族函式,能構建健壯、可維護的錯誤處理體系。