C言語の標準ライブラリには、エラー番号から人間が読めるメッセージ文字列を取得するstrerror関数があります。
システムコールやライブラリ関数が返すerrnoと組み合わせることで、デバッグやログ出力が格段にやりやすくなります。
本記事では、strerrorの基本的な使い方から注意点、実用的なサンプル、代表的なエラーコード一覧まで、コンパクトに整理して解説します。
strerrorとは
strerrorの概要
strerrorは、C言語の標準ライブラリ関数で、エラー番号(整数)を説明文の文字列に変換するために使用します。
主にerrnoと組み合わせて、処理が失敗した理由を人間にわかりやすく表示する目的で使われます。
関数プロトタイプ
#include <string.h>
char *strerror(int errnum);
- 引数
errnum
取得したいエラー番号を指定します。通常はerrnoを渡します。 - 戻り値
エラー内容を表すNULL終端文字列へのポインタを返します。
ポイントは「エラー番号→説明文」の変換器として働くということです。

インクルードヘッダと関連するシンボル
strerrorを使うには#include <string.h>が必要です。
あわせて、エラー番号を扱うerrnoや各種エラー定数を使う場合は#include <errno.h>もインクルードします。
#include <stdio.h>
#include <string.h>
#include <errno.h>
実務ではこの3つのヘッダをセットで使うことがほとんどです。
strerrorの基本的な使い方
errnoと組み合わせた典型パターン
最もよくある使い方は、システムコールやライブラリ関数が失敗した直後に、errnoをstrerrorに渡してエラー内容を表示するパターンです。

サンプル1: fopenのエラーをstrerrorで表示する
#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を正しく運用するためには、次のルールを守ることが重要です。
- エラーを起こした関数の直後にerrnoを読む
間に別のライブラリ関数を挟むとerrnoが上書きされる可能性があります。 - errnoがセットされるのは「失敗したとき」だけ
成功したときは値が更新されない場合が多く、過去の値が残っていることがあります。 - エラー状況をログ出力したい場合は、errnoとstrerrorの両方を記録する
数値(例:2)と文字列(例:”No such file or directory”)の両方があると解析しやすくなります。
strerrorの戻り値と注意点
戻り値の寿命とスレッド安全性
strerrorの戻り値は静的な領域にある文字列へのポインタです。
そのため、次のような性質があります。
- 呼び出しのたびに新しいメモリを確保しているわけではない
- ある実装では、後続のstrerror呼び出しで内容が上書きされる場合がある
- スレッドセーフではない実装が多い
したがって、マルチスレッドプログラムでは、標準のstrerrorをそのまま使うのは推奨されません。
代わりに後述するstrerror_rやstrerror_sを使うことが多いです。

戻り値の書き換え禁止
strerrorが返した文字列へのポインタは読み取り専用だと考えるべきです。
戻り値を書き換えようとすると、未定義動作となる可能性があります。
char *msg = strerror(errno);
/* msg[0] = 'X'; // これは絶対にやってはいけない */
戻り値は「表示する」「ログに書き出す」「自前のバッファにコピーする」用途に限定し、直接編集することは避けてください。
strerrorの実用的なサンプル
サンプル2: 汎用的なエラーログ出力関数
エラー処理が増えてくると、毎回printf("errno=%d, %s", errno, strerror(errno));と書くのは面倒です。
そこで、汎用的なエラーログ関数を用意しておくと便利です。

#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種類の仕様がある点に注意が必要です。
#include <string.h>
int strerror_r(int errnum, char *buf, size_t buflen);
- 引数
buf
ユーザーが用意したバッファへのポインタ - 引数
buflen
バッファサイズ - 戻り値
0なら成功、0以外ならエラー番号
メッセージを自前のバッファに書き込んでもらうことで、スレッドごとに独立したエラーメッセージを扱えるようになります。
サンプル3: strerror_rの利用例(Linux想定)
#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というバッファ長安全版が用意されています。
#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の例(英語) | 主な意味 |
|---|---|---|---|
| EPERM | 1 | Operation not permitted | 操作が許可されていない |
| ENOENT | 2 | No such file or directory | ファイルやディレクトリが存在しない |
| ESRCH | 3 | No such process | 指定されたプロセスが存在しない |
| EINTR | 4 | Interrupted system call | システムコールが割り込まれた |
| EIO | 5 | I/O error | 入出力エラー |
| ENXIO | 6 | No such device or address | デバイスまたはアドレスが存在しない |
| E2BIG | 7 | Argument list too long | 引数リストが長すぎる |
| ENOEXEC | 8 | Exec format error | 実行形式として不正 |
| EBADF | 9 | Bad file descriptor | 不正なファイルディスクリプタ |
| ECHILD | 10 | No child processes | 子プロセスが存在しない |
| EAGAIN | 11 | Resource temporarily unavailable | 一時的にリソースが利用できない |
| ENOMEM | 12 | Cannot allocate memory | メモリ確保に失敗 |
| EACCES | 13 | Permission denied | アクセス権限がない |
| EFAULT | 14 | Bad address | 不正なアドレス |
| EBUSY | 16 | Device or resource busy | デバイスやリソースがビジー |
| EEXIST | 17 | File exists | ファイルが既に存在する |
| EXDEV | 18 | Invalid cross-device link | デバイスをまたぐリンクは不正 |
| ENODEV | 19 | No such device | そのようなデバイスは存在しない |
| ENOTDIR | 20 | Not a directory | ディレクトリではない |
| EISDIR | 21 | Is a directory | ディレクトリである |
| EINVAL | 22 | Invalid argument | 不正な引数 |
| ENFILE | 23 | Too many open files in system | システム全体で開いているファイルが多すぎる |
| EMFILE | 24 | Too many open files | プロセスで開いているファイルが多すぎる |
| ENOSPC | 28 | No space left on device | デバイスの空き容量がない |
| EPIPE | 32 | Broken pipe | パイプが切断されている |
| ERANGE | 34 | Numerical result out of range | 数値が範囲外 |
エラー番号そのものはerrno.hに定義されています。
必要に応じて、開発環境のドキュメントと合わせて確認するとよいです。
簡易的な「errno→メッセージ」ダンプツール
学習用に、0〜いくつかのエラー番号に対してstrerrorを呼び、どんなメッセージが返ってくるかを一覧表示してみるのも有効です。
#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を確認するタイミングを間違える
次のようなコードは誤りです。
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の戻り値を、そのままグローバル変数などに保持し続けるのも危険です。
// NG例
const char *g_last_error;
void foo(void) {
if (something_failed()) {
g_last_error = strerror(errno); // ポインタだけ保存
}
}
この場合、後続のstrerror呼び出しによりg_last_errorが指す内容が意図せず変化する可能性があります。
必要なら自前のバッファにコピーしてください。
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_rやstrerror_sなどのスレッドセーフな代替を使用するとよいです。
代表的なerrnoとメッセージの対応関係を把握し、共通のエラーログ出力関数を用意しておけば、実務レベルのエラー処理がシンプルになり、保守性の高いCプログラムを記述しやすくなります。
