エラー番号が入るerrno

こんにちは、めのんです!

昨日は1日お休みをいただきました。
ここのところ忙しくてバタバタしている日も多くて、気がつくと遅い時間になってしまってブログが書けないんですよね。
今日も危なかったんですけど、どうにかこうにか書いています。

前回、そろそろワイド文字について解説したいということを書きました。
ところがよく考えると、ワイド文字を扱うにはそれより前にロケールを解説しないといけません。
ロケールを扱うにはそれより前に整数型の最大値と最小値を解説する必要がありそうです。

それとワイド文字を扱う上で必要になってくるのがエラーエラー番号です。

errnoマクロ

Cではエラー番号を格納する場所としてerrnoというマクロが定義されています。
errnoはマクロですけど、見た目上はいわゆるグローバル変数として振る舞います。

すなわち、「errno.h」ヘッダを取り込めばいつでもどこでも参照することができ、エラー番号を代入することができます。

実際にはerrnoは単純なオブジェクトではなく、次のように内部で関数を呼び出していることも多いようです。

int *__errno(void);
#define errno  (*__errno())

関数呼び出しにする理由は、たとえばマルチスレッドの環境でスレッドごとに異なるエラー番号をできるようにするためだと思われます(C11より前のバージョンで実際にスレッドセーフかどうかは処理系によります)。

このerrnoは標準ライブイラリの関数では決してゼロに設定されないことが保証されています。
ですので、errnoを設定する可能性がある関数を呼び出す前に自分でゼロを書き込んでおくと、エラーが発生したかどうかを検出できるようになっています。

たとえば、平方根を求めるsqrt関数にマイナスの値を渡すと定義域エラーが発生し、errnoに「EDOM」が設定されます。
ですので、平方根を求める際には次のようにします。

#include <stdio.h>
#include <math.h>
#include <errno.h>

int main(void)
{
  double x;
  scanf("%lf", &x);

  errno = 0;  // 先に0を設定しておく
  double y = sqrt(x);
  if (errno != 0)  // errnoが0でなければエラー
    printf("eror: %d\n", errno);
  else
    printf("%g\n", y);
  return 0;
}

エラー番号の種類M

エラー番号にどんなものがあるかは処理系ごとに変わってきます。
確実にいえることは、ゼロであればエラーがなかったということです。

また、標準規格の範囲で次の3つのエラー番号が定義されています。

EDOM
定義域エラー

ERANGE
値域エラー

EILSEQ
表現形式エラー

これらの値はすべて「errno.h」ヘッダでマクロとして定義されています。

エラー番号の文字列化

さきほどのサンプルコードのように、errnoの値をprintf関数などで出力することはできますが、単なる整数値なのでそれが何を意味するかは決してわかりやすいとはいえません。

この問題を解消するために、errnoの値を人間が理解しやすい文字列に変換するための関数が用意されています。

strerror関数

「strerror関数」は「string.h」ヘッダで次のように宣言されています。

char *strerror(int errnum);

このstrerror関数にerrnoの値を渡すと、その値に対応する文字列が返されます。
一応、規格上ではロケールに依存することになっていますが、ほとんどの処理系は英語の文字列を返すだけのようです。

perror関数

「perror関数」はerrnoの値に対応した文字列を標準エラーに出力するためのものです。
perror関数は「stdio.h」ヘッダで次のように宣言されています。

void perror(const char *s);

sがNULLまたは空文字列でなければ、まずはじめにsとそのあとにコロン(:)そして空白を1つ標準エラーに出力します。
そのあと、strerror(s)の結果と改行を標準エラーに出力します。

perror関数は次のように実装されていると考えてよいでしょう。

#include <stdio.h>
#include <string.h>
#include <errno.h>

void perror(const char *s)
{
  if (s != NULL && *s != '\0')
    fprintf(stderr, "%s: ", s);
  fprintf(stderr, "%s\n", strerror(errno));
}

というように、Cのエラー通知は非常にシンプルな形で行われます。
あとはこれらのマクロや関数を駆使して、プログラマーが自分でエラー処理を実装する必要があります。


今回の解説は以上となります。
次回はできれば整数型の最大値と最小値を解説したいと思います。
どうぞご期待ください!