閉じる

【C言語】strerrorの使い方とエラー文一覧【完全版】

C言語の標準ライブラリには、エラー番号から人間が読めるメッセージ文字列を取得するstrerror関数があります。

システムコールやライブラリ関数が返すerrnoと組み合わせることで、デバッグやログ出力が格段にやりやすくなります。

本記事では、strerrorの基本的な使い方から注意点、実用的なサンプル、代表的なエラーコード一覧まで、コンパクトに整理して解説します。

strerrorとは

strerrorの概要

strerrorは、C言語の標準ライブラリ関数で、エラー番号(整数)を説明文の文字列に変換するために使用します。

主にerrnoと組み合わせて、処理が失敗した理由を人間にわかりやすく表示する目的で使われます。

関数プロトタイプ

C言語
#include <string.h>

char *strerror(int errnum);
  • 引数errnum
    取得したいエラー番号を指定します。通常はerrnoを渡します。
  • 戻り値
    エラー内容を表すNULL終端文字列へのポインタを返します。

ポイントは「エラー番号→説明文」の変換器として働くということです。

インクルードヘッダと関連するシンボル

strerrorを使うには#include <string.h>が必要です。

あわせて、エラー番号を扱うerrnoや各種エラー定数を使う場合は#include <errno.h>もインクルードします。

C言語
#include <stdio.h>
#include <string.h>
#include <errno.h>

実務ではこの3つのヘッダをセットで使うことがほとんどです。

strerrorの基本的な使い方

errnoと組み合わせた典型パターン

最もよくある使い方は、システムコールやライブラリ関数が失敗した直後に、errnoをstrerrorに渡してエラー内容を表示するパターンです。

サンプル1: fopenのエラーをstrerrorで表示する

C言語
#include <stdio.h>
#include <string.h>  // strerrorの宣言
#include <errno.h>   // errnoとエラー番号定数

int main(void) {
    const char *filename = "no_such_file.txt";

    // 存在しないファイルを読み取り専用で開いてみる
    FILE *fp = fopen(filename, "r");
    if (fp == NULL) {
        // ここでerrnoにはエラー番号が格納されている
        int err = errno;  // 必要ならローカル変数に退避しておく

        // エラー番号とエラーメッセージを表示
        printf("ファイル \"%s\" を開けませんでした。\n", filename);
        printf("errno = %d\n", err);
        printf("strerror: %s\n", strerror(err));
        return 1;
    }

    // 成功時の処理(ここでは実装省略)
    fclose(fp);
    return 0;
}

想定される出力例(環境により異なります)。

実行結果
ファイル "no_such_file.txt" を開けませんでした。
errno = 2
strerror: No such file or directory

このように、errnoの整数値だけでは意味がわかりにくいところを、strerror人間が理解しやすいメッセージに変換してくれます。

使うときの基本ルール

strerrorを正しく運用するためには、次のルールを守ることが重要です。

  1. エラーを起こした関数の直後にerrnoを読む
    間に別のライブラリ関数を挟むとerrnoが上書きされる可能性があります。
  2. errnoがセットされるのは「失敗したとき」だけ
    成功したときは値が更新されない場合が多く、過去の値が残っていることがあります。
  3. エラー状況をログ出力したい場合は、errnoとstrerrorの両方を記録する
    数値(例:2)と文字列(例:”No such file or directory”)の両方があると解析しやすくなります。

strerrorの戻り値と注意点

戻り値の寿命とスレッド安全性

strerrorの戻り値は静的な領域にある文字列へのポインタです。

そのため、次のような性質があります。

  • 呼び出しのたびに新しいメモリを確保しているわけではない
  • ある実装では、後続のstrerror呼び出しで内容が上書きされる場合がある
  • スレッドセーフではない実装が多い

したがって、マルチスレッドプログラムでは、標準のstrerrorをそのまま使うのは推奨されません

代わりに後述するstrerror_rstrerror_sを使うことが多いです。

戻り値の書き換え禁止

strerrorが返した文字列へのポインタは読み取り専用だと考えるべきです。

戻り値を書き換えようとすると、未定義動作となる可能性があります。

C言語
char *msg = strerror(errno);
/* msg[0] = 'X';  // これは絶対にやってはいけない */

戻り値は「表示する」「ログに書き出す」「自前のバッファにコピーする」用途に限定し、直接編集することは避けてください。

strerrorの実用的なサンプル

サンプル2: 汎用的なエラーログ出力関数

エラー処理が増えてくると、毎回printf("errno=%d, %s", errno, strerror(errno));と書くのは面倒です。

そこで、汎用的なエラーログ関数を用意しておくと便利です。

C言語
#include <stdio.h>
#include <string.h>
#include <errno.h>

// 共通のエラーログ出力関数
void log_error(const char *tag, const char *detail) {
    int err = errno;  // 先に退避しておく
    fprintf(stderr,
            "[ERROR] %s: %s (errno=%d, msg=%s)\n",
            tag,
            detail,
            err,
            strerror(err));  // ここでerrを使うのがポイント
}

int main(void) {
    const char *filename = "no_such_file.txt";
    FILE *fp = fopen(filename, "r");
    if (fp == NULL) {
        log_error("FILE_OPEN", "ファイルを開けませんでした");
        return 1;
    }

    fclose(fp);
    return 0;
}
実行結果
[ERROR] FILE_OPEN: ファイルを開けませんでした (errno=2, msg=No such file or directory)

このように、共通のエラーログ関数を1つ作っておき、内部でstrerrorを呼ぶと、アプリ全体で統一された形式のログを出力できるようになります。

マルチスレッド環境での代替: strerror_rなど

POSIX系: strerror_r

POSIX環境(Linuxなど)では、スレッドセーフなエラーメッセージ取得関数としてstrerror_rが提供されています。

ただし、実装により2種類の仕様がある点に注意が必要です。

C言語
#include <string.h>

int strerror_r(int errnum, char *buf, size_t buflen);
  • 引数buf
    ユーザーが用意したバッファへのポインタ
  • 引数buflen
    バッファサイズ
  • 戻り値
    0なら成功、0以外ならエラー番号

メッセージを自前のバッファに書き込んでもらうことで、スレッドごとに独立したエラーメッセージを扱えるようになります。

サンプル3: strerror_rの利用例(Linux想定)

C言語
#include <stdio.h>
#include <string.h>
#include <errno.h>

int main(void) {
    const char *filename = "no_such_file.txt";
    FILE *fp = fopen(filename, "r");
    if (fp == NULL) {
        int err = errno;
        char buf[128];  // エラーメッセージ格納用バッファ

        // スレッドセーフにメッセージを取得
        if (strerror_r(err, buf, sizeof(buf)) == 0) {
            printf("fopen失敗: errno=%d, msg=%s\n", err, buf);
        } else {
            printf("fopen失敗: errno=%d, msg=不明なエラー\n", err);
        }
    }

    return 0;
}
実行結果
fopen失敗: errno=2, msg=No such file or directory

Windows系: strerror_s

Windows(MSVC)ではstrerror_sというバッファ長安全版が用意されています。

C言語
#include <string.h>
errno_t strerror_s(char *buf, size_t buflen, int errnum);
  • 戻り値errno_tが0なら成功、それ以外はエラーです。
  • POSIXのstrerror_rとよく似た使い方です。

マルチスレッドやセキュリティ要件が厳しいコードでは、これらの関数を積極的に使うようにしてください。

主なエラー番号とstrerrorが返すメッセージ例

代表的なerrnoとメッセージ一覧

strerrorが返す具体的な文字列はOSや実装によって異なる可能性がありますが、概ね次のようなメッセージになります。

ここでは、よく使われる代表的なエラーコードを中心に一覧にします。

※メッセージは典型的なLinux系の例です。

実際の環境では多少異なることがあります。

errno定数数値例strerrorの例(英語)主な意味
EPERM1Operation not permitted操作が許可されていない
ENOENT2No such file or directoryファイルやディレクトリが存在しない
ESRCH3No such process指定されたプロセスが存在しない
EINTR4Interrupted system callシステムコールが割り込まれた
EIO5I/O error入出力エラー
ENXIO6No such device or addressデバイスまたはアドレスが存在しない
E2BIG7Argument list too long引数リストが長すぎる
ENOEXEC8Exec format error実行形式として不正
EBADF9Bad file descriptor不正なファイルディスクリプタ
ECHILD10No child processes子プロセスが存在しない
EAGAIN11Resource temporarily unavailable一時的にリソースが利用できない
ENOMEM12Cannot allocate memoryメモリ確保に失敗
EACCES13Permission deniedアクセス権限がない
EFAULT14Bad address不正なアドレス
EBUSY16Device or resource busyデバイスやリソースがビジー
EEXIST17File existsファイルが既に存在する
EXDEV18Invalid cross-device linkデバイスをまたぐリンクは不正
ENODEV19No such deviceそのようなデバイスは存在しない
ENOTDIR20Not a directoryディレクトリではない
EISDIR21Is a directoryディレクトリである
EINVAL22Invalid argument不正な引数
ENFILE23Too many open files in systemシステム全体で開いているファイルが多すぎる
EMFILE24Too many open filesプロセスで開いているファイルが多すぎる
ENOSPC28No space left on deviceデバイスの空き容量がない
EPIPE32Broken pipeパイプが切断されている
ERANGE34Numerical result out of range数値が範囲外

エラー番号そのものはerrno.hに定義されています。

必要に応じて、開発環境のドキュメントと合わせて確認するとよいです。

簡易的な「errno→メッセージ」ダンプツール

学習用に、0〜いくつかのエラー番号に対してstrerrorを呼び、どんなメッセージが返ってくるかを一覧表示してみるのも有効です。

C言語
#include <stdio.h>
#include <string.h>

int main(void) {
    // 0〜40番までをざっくり表示してみる(実際の有効範囲は環境依存)
    for (int err = 0; err <= 40; ++err) {
        printf("err=%2d: %s\n", err, strerror(err));
    }
    return 0;
}

出力は環境ごとに異なりますが、自分の環境でどのエラー番号にどんなメッセージが対応しているかを確認する簡易ツールとして役立ちます。

よくある落とし穴とベストプラクティス

errnoを確認するタイミングを間違える

次のようなコードは誤りです。

C言語
FILE *fp = fopen("no_such_file.txt", "r");
if (fp == NULL) {
    // 誤: ここで別のライブラリ関数を先に呼び出してしまう
    printf("何かログ...\n");

    // その後でerrnoを使うと、値が変わっている可能性がある
    printf("errno=%d, msg=%s\n", errno, strerror(errno));
}

errnoはできるだけ「失敗した直後の行」で読む、あるいはすぐにローカル変数に退避しておくことが重要です。

strerrorの戻り値を長期保存してしまう

strerrorの戻り値を、そのままグローバル変数などに保持し続けるのも危険です。

C言語
// NG例
const char *g_last_error;

void foo(void) {
    if (something_failed()) {
        g_last_error = strerror(errno);  // ポインタだけ保存
    }
}

この場合、後続のstrerror呼び出しによりg_last_errorが指す内容が意図せず変化する可能性があります。

必要なら自前のバッファにコピーしてください。

C言語
char g_last_error[128];

void foo(void) {
    if (something_failed()) {
        snprintf(g_last_error, sizeof(g_last_error),
                 "%s", strerror(errno));  // 文字列としてコピー
    }
}

まとめ

strerrorは、整数のエラー番号を人間が理解しやすいメッセージ文字列に変換するための標準関数です。

errnoと組み合わせることで、ファイル操作やシステムコールの失敗理由を明確にログ出力でき、デバッグ効率が大きく向上します。

戻り値が静的な領域を指すことやスレッド非安全である点には注意し、必要に応じてstrerror_rstrerror_sなどのスレッドセーフな代替を使用するとよいです。

代表的なerrnoとメッセージの対応関係を把握し、共通のエラーログ出力関数を用意しておけば、実務レベルのエラー処理がシンプルになり、保守性の高いCプログラムを記述しやすくなります。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!