Skip to main content

Command Palette

Search for a command to run...

C Error Handling: errno

Updated
1 min read

最近碰到的坑,但查了下發現是基礎概念,整理記錄一下。

背景: 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:

  1. 函數並不會主動清乾淨 errno

  2. 只有在函數告訴你有錯(例如回傳的指標 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;
}

More from this blog

簡介 C++ 的 Type Erase (用多型和模板做 Duck Type)

起點 讓我們先從 template 出發:foo 需要一個 callback function。 template<typename Func> void foo(Func callback) { // ... callback(); } 但是這會讓編譯錯誤訊息有點模糊:假如 callback 並不是一個可以呼叫的函數指標,或者並不是一個 callable object ,那編譯器會說錯出在第四行。但是我們都希望,編譯器在呼叫函數時就幫我們指出:這不是 foo 想要的 call...

May 14, 20243 min read

帕秋莉的魔法筆記

45 posts

後端工程師。

不定時張貼一些寫扣時的筆記。