数値のエラー番号だけでは、何が起きたか初心者には分かりづらいものです。
C言語のstrerrorは、その番号に対応する人間が読めるメッセージを返してくれる関数です。
本記事では使い方と注意点、活用方法を丁寧に解説します。
これを覚えると、エラー表示やログ出力の品質が一気に上がります。
C言語のstrerrorとは?
できることと目的
strerrorは、エラー番号(int)を人間が読める説明文字列に変換する関数です。
標準ライブラリ関数が失敗したときなどに参照するエラー番号を、ユーザーや開発者が理解しやすい文章に直してくれます。
これにより、単なる数値ではなく意味のある説明(例: No such file or directory)を画面表示やログに残せます。
典型的な利用場面
ファイルを開けない、無効な引数が渡された、アクセス権が足りない、といった失敗時に、errnoの値をstrerrorで可読化し、診断や報告に使います。
ユーザー向けのメッセージ表示、標準エラーへの出力、ログ保存などが代表例です。
関連する概念
エラー番号の取得元は多くの場合errnoです。
具体的な番号定数(例: ENOENT, EINVAL)は<errno.h>で定義されています。
本記事ではstrerrorの使い方に焦点を当て、errnoやperror、assertの詳細は別記事で扱います。
必要なヘッダー
宣言は<string.h>にあります。
また、エラー番号(マクロやerrno)を使うなら<errno.h>、出力にprintfなどを使うなら<stdio.h>も併せてインクルードします。
ヘッダーの役割一覧
| ヘッダー | 役割 |
|---|---|
#include <string.h> | strerrorの宣言 |
#include <errno.h> | errnoとエラー番号マクロ |
#include <stdio.h> | 入出力(表示やログ書き込み) |
include例
#include <string.h> // strerrorの宣言
#include <errno.h> // errnoやENOENTなどのマクロ
#include <stdio.h> // printf, fprintf
関数シグネチャ(char *strerror(int))
// C標準で規定されるstrerror
char *strerror(int errnum);
引数と返り値
- 引数
errnum: メッセージに変換したいエラー番号です。 - 返り値: 対応する説明文字列を指す
char *です。この文字列は内部の静的領域にあり、変更やfreeはしてはいけません。
規格と移植性
strerrorはC標準に含まれますが、スレッドセーフ性は規定されていません。
POSIX系ではstrerror_r、Windowsではstrerror_sが用意されています。
メッセージの内容や言語は環境(実装・ロケール)に依存します。
strerrorの使い方
エラー番号を用意する
エラー番号は、多くの場合、直前のライブラリ関数の失敗後にerrnoから得ます。
成功時にerrnoは更新されないことがあるため、失敗を検知した直後に一度ローカル変数へ退避しておくのが安全です。
よくあるパターン
FILE *fp = fopen("missing.txt", "r");
if (!fp) {
int saved_errno = errno; // 直後に退避するのがポイント
const char *msg = strerror(saved_errno);
printf("失敗: %s\n", msg);
}
メッセージ文字列を取得する
strerrorを呼び、返ってきたポインタを使って表示やログ記録を行います。
返る文字列は内部バッファで、書き換えやfreeは厳禁です。
連続呼び出しやマルチスレッドでは内容が上書きされ得る点に注意します。
文字列の寿命と取り扱い
- 必要なら自前のバッファへコピーします。例:
snprintf(buf, sizeof buf, "%s", strerror(err)); - 長期保持やスレッド間共有が必要な場面では
strerror_r/strerror_sの利用を検討します。
以下は存在しないファイルを開いてエラーにし、そのエラー番号からstrerrorでメッセージを取得して表示する最小例です。
#include <stdio.h> // printf, fprintf
#include <errno.h> // errno, ENOENTなど
#include <string.h> // strerror
int main(void) {
// わざと存在しないファイルを開いて失敗させる
const char *path = "no_such_file.txt";
FILE *fp = fopen(path, "r");
if (!fp) {
// 失敗直後にerrnoを退避
int saved_errno = errno;
// エラーメッセージ文字列を取得
const char *msg = strerror(saved_errno);
// 画面に表示(標準出力)
printf("open failed for %s\n", path);
printf("errno=%d, message=%s\n", saved_errno, msg);
// 標準エラーにも出力
fprintf(stderr, "ERROR: %s (errno=%d)\n", msg, saved_errno);
return 1;
}
// 成功時は後始末
fclose(fp);
return 0;
}
出力例(環境により異なります):
open failed for no_such_file.txt
errno=2, message=No such file or directory
ERROR: No such file or directory (errno=2)
メッセージの言語や文言はOSやロケール設定で変わる点に注意してください。
strerrorの活用例
画面に表示する
ユーザーに状況を伝えるため、説明文とあわせてメッセージを出すと親切です。
番号だけを出すのではなく、何が起きたのかを補足する一文を加えると理解されやすくなります。
表示例のコード断片
// 具体的な操作の説明と、strerrorのメッセージを併記
printf("設定ファイルを読み込めませんでした: %s\n", strerror(saved_errno));
標準エラーに出力する(fprintf(stderr,…))
エラーは通常、標準エラーstderrへ出力します。
標準出力と分けることで、リダイレクトやパイプ処理でもログが失われにくくなります。
stderr出力のコード断片
// エラーはstderrへ。ツールやCIからの利用で特に有益です
fprintf(stderr, "ERROR: %s (errno=%d)\n", strerror(saved_errno), saved_errno);
ログに残す
長期の調査やサポートのために、タイムスタンプと合わせてログ保存しておくと便利です。
ログ出力のサンプルプログラム
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>
static void log_error(const char *tag, int errnum) {
// ログファイルを追記モードで開く
FILE *log = fopen("app.log", "a");
if (!log) {
// ログが開けない場合は標準エラーにフォールバック
fprintf(stderr, "log open failed: %s\n", strerror(errno));
return;
}
// タイムスタンプを作る
time_t t = time(NULL);
struct tm *lt = localtime(&t);
char ts[32];
if (lt) {
strftime(ts, sizeof ts, "%Y-%m-%d %H:%M:%S", lt);
} else {
snprintf(ts, sizeof ts, "unknown-time");
}
// メッセージを取得してログに書く
const char *msg = strerror(errnum);
fprintf(log, "[%s] %s: %s (errno=%d)\n", ts, tag, msg, errnum);
fclose(log);
}
int main(void) {
// 存在しないファイルを開いてエラーを発生させる
FILE *fp = fopen("missing.dat", "r");
if (!fp) {
int saved_errno = errno; // すぐに退避
log_error("open", saved_errno);
fprintf(stderr, "ログへ記録しました: %s\n", strerror(saved_errno));
return 1;
}
fclose(fp);
return 0;
}
出力例(標準エラーとログファイル):
ログへ記録しました: No such file or directory
# app.log の一例
[2025-05-01 12:34:56] open: No such file or directory (errno=2)
strerrorの注意点
返される文字列は変更しない
返ってくるポインタはライブラリ内部の静的領域を指しています。
書き換えたりfreeしたりしてはいけません。
必要なら自前のバッファへコピーします。
NGとOKの例
char *p = strerror(ENOENT);
// NG: 未定義動作。内部領域を書き換えることになる
// p[0] = 'X';
// OK: 自前のバッファへコピーして扱う
char buf[128];
snprintf(buf, sizeof buf, "%s", p);
// bufは自由に加工してよい
連続呼び出しで上書きに注意
多くの実装でstrerrorは同じ内部バッファを再利用するため、連続呼び出しで前の結果が上書きされます。
問題例と対策
// 問題例: 2回の呼び出しで同じバッファが使われる可能性
printf("%s | %s\n", strerror(EINVAL), strerror(ENOENT));
// 対策: 自前バッファへ退避してから使う
char a[128], b[128];
snprintf(a, sizeof a, "%s", strerror(EINVAL));
snprintf(b, sizeof b, "%s", strerror(ENOENT));
printf("%s | %s\n", a, b);
マルチスレッドはstrerror_rも検討
スレッドごとの安全性が必要ならstrerror_r(POSIX)やstrerror_s(Windows)を使います。
いずれも呼び出し側が用意したバッファへメッセージを書き込みます。
POSIXの例(strerror_r)
#include <string.h>
#include <errno.h>
#include <stdio.h>
void describe_error_posix(int errnum) {
char buf[256]; // 呼び出し側のバッファ
// POSIX XSI版: 戻り値はint(0で成功)
int rc = strerror_r(errnum, buf, sizeof buf);
if (rc == 0) {
printf("POSIX: %s (errno=%d)\n", buf, errnum);
} else {
printf("POSIX: failed to get message (errno=%d)\n", errnum);
}
}
GNU libcには戻り値がchar *の別版があり、挙動が異なります。
移植性を優先するならXSI準拠の形を使うか、実装ごとに条件分岐します。
Windowsの例(strerror_s)
#include <string.h>
#include <errno.h>
#include <stdio.h>
void describe_error_win(int errnum) {
char buf[256];
// Windows/Microsoft拡張(安全なバージョン)
errno_t e = strerror_s(buf, sizeof buf, errnum);
if (e == 0) {
printf("WIN: %s (errno=%d)\n", buf, errnum);
} else {
printf("WIN: failed to get message (errno=%d)\n", errnum);
}
}
WindowsのWin32 APIのエラー番号(GetLastError)はerrnoとは別物です。
その場合はFormatMessageを使います(本記事の範囲外)。
環境によりメッセージ内容が異なる
メッセージの言語と文言はOS・Cライブラリ実装・ロケール設定に依存します。
同じENOENTでも「No such file or directory」や日本語メッセージになることがあります。
代表的なエラー番号と典型メッセージの例
| 番号の例 | マクロ | 典型的な英語メッセージ(一例) |
|---|---|---|
| 2 | ENOENT | No such file or directory |
| 13 | EACCES | Permission denied |
| 22 | EINVAL | Invalid argument |
実際の表示は環境次第で、この表はあくまで参考です。
ロケールの影響を見るミニコード
#include <locale.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
int main(void) {
// 環境のロケールに従う
setlocale(LC_ALL, "");
printf("ENOENT: %s\n", strerror(ENOENT));
return 0;
}
出力例(一例):
ENOENT: No such file or directory
まとめ
strerrorは、エラー番号を人間が読める説明文へ変換する基本かつ強力な関数です。
使うときは、失敗直後にerrnoを退避し、返される文字列を変更・解放しないこと、連続呼び出しやマルチスレッドでの上書きに注意することが大切です。
スレッドセーフ性が必要ならstrerror_rやstrerror_sを選び、表示先(標準出力・標準エラー・ログ)を目的に合わせて使い分けてください。
環境によって文言が変わる点も踏まえ、ユーザーにも開発者にも分かりやすいエラーメッセージを提供していきましょう。
