C Error Handling: errno
最近碰到的坑,但查了下發現是基礎概念,整理記錄一下。
背景: gmtime_r
他會從傳進去的 time_t
算出年月日、星期、時分秒。這並不是件簡單的事情,除了閏年外,假如考慮進時區的話,還要計入日光節約時間,會需要讀取一些定期更新的檔案。
gmtime_r
的細節不是這文章的重點,只在這裡大概說一下它的功能。
使用並且處理
#include <ctime>
#include <cstdio>
#include <errno.h>
int main() {
time_t tt = 0;
std::tm tm0;
errno = 0;
gmtime_r(&tt, &tm0);
if (errno != 0) {
perror("gmtime_r");
}
return 0;
}
這在 docker ubuntu 16.04 上會遇到:
gmtime_r: No such file or directory
但是在非 docker 的 ubuntu 16.04 或者 docker ubuntu 20.04 或者 MacOS。
一開始,我以爲可能是類似 timedatectl
的問題,因為他也有類似的現象:
root@618d495a44a6:~# timedatectl status
Failed to create bus connection: No such file or directory
在中間過程他似乎會去尋找某個檔案。雖然這有點亂槍打鳥,但有沒有可能他們用到相同的檔案?
所以我嘗試了這個做法,雖然 timedatectl
可行,但最上面的程式仍然會噴錯誤。
errno
的原則和 gmtime_r
的文件
後來,我重新翻了 APUE 的最前面,關於 errno 的必需要知道的事情,才發現我大概錯誤的使用了 errno:
函數並不會主動清乾淨 errno
只有在函數告訴你有錯(例如回傳的指標 spec 上說明錯誤會是 NULL),才可以去檢查 errno (或者說,這時 errno 才有意義)
也就是說,有可能一個系統函數設計這樣:讀取某個固定路徑的檔案,假如該檔案沒有,就用其他預設值,不需要回傳錯誤。
在沒有該檔案時, errno 會被 open 設置,但是函數並不會在回傳預設值前清乾淨 errno。
最後,根據 gmtime_r
的文件,直接判斷回傳指標是否為 NULL 才是正確的作法。
#include <ctime>
#include <cstdio>
#include <errno.h>
int main() {
time_t tt = 0;
std::tm tm0;
if (!gmtime_r(&tt, &tm0)) {
perror("gmtime_r");
}
return 0;
}