4

1 つの入力と 1 つの出力 (厳密には 1 つではない) を持つ関数は、呼び出されたときにメッセージを出力してはならないと言われました。しかし、私は理解していません。セキュリティのためですか、それとも慣習のためですか?

例を挙げましょう。シーケンシャル リストのデータに不正なインデックスでアクセスしようとした場合の対処方法は?

// 1. Give out the error message inside the function directly.
DataType GetData(seqList *L, int index)
{
    if (index < 0  ||  index >= L->length) {
        printf("Error: Access beyond bounds of list.\n");
        // exit(EXIT_FAILURE);
    }
    return L->data[index];
}

// 2. Return a value or use a global variable(like errno) that
//    indicates whether the function performs successfully.
StateType GetData(seqList *L, int index, int *data)
{
    if (index < 0  ||  index >= L->length) {
        return ERROR;
    }
    *data = L->data[index];
    return OK;
}
4

6 に答える 6

6

ここでは次の 2 つのことが起こっていると思います。

  1. ストリームへの書き込みなど、目に見える予期しない副作用は一般的に悪いものであり、1 つの入力と 1 つの出力を持つ関数だけではありません。リスト ライブラリを使用していて、通常の出力に使用していたのと同じ出力ストリームにエラー メッセージを静かに書き始めたら、それは問題だと思います。ただし、そのような関数を自分の個人的な使用のために作成していて、実行したいアクションが常にメッセージとを出力することであることが事前にわかっている場合は、問題ありませんexit()。この行動を他の人に強制しないでください。

  2. これは、呼び出し元にエラーを通知する方法に関する一般的な問題の特定のケースです。多くの場合、関数はエラーに対する正しい応答を認識できません。これは、関数が呼び出し元のコンテキストを持っていないためです。たとえばmalloc()、ほとんどの場合、malloc()失敗した場合は単に終了したいだけですが、まれに、malloc()失敗するまで呼び出すことで故意にメモリをいっぱいにしてから、別のことをしたい場合があります。この場合、関数が終了するかどうかを決定する必要はありません。失敗したことを通知してから、制御を私に戻してほしいだけです。

ライブラリ関数でエラーを処理するには、さまざまな方法があります。

  1. 終了 - 自分でプログラムを作成している場合は問題ありませんが、汎用ライブラリ関数には適していません。一般に、ライブラリ関数の場合、エラーが発生した場合の対処方法を呼び出し元に決定させる必要があるため、関数の役割は呼び出し元にエラーを通知することに限定されます。

  2. エラー値を返します - 場合によっては問題ありませんが、実行可能なエラー値がない場合もあります。atoi()は適切な例です。返される可能性のあるすべての値は、入力文字列の正しい翻訳である可能性があります。0エラーが発生した場合に何を返すかは関係ありません。エラーと-1有効な結果を区別する方法はありません。これが、エラーが発生した場合に未定義の動作が発生する理由です。また、少し純粋主義的な観点から見ると、意味的にも疑問があります。平方根ではなくエラーコードを返すことは別のことです。戻り値が 2 つの完全に異なる目的を果たす場合、関数の自己文書化された単純さが失われる可能性があります。

  3. を設定するなど、プログラムをエラー状態のままにしますerrno。実行可能な戻り値がない場合、関数はエラーが発生したことを通知できないという根本的な問題がまだ残っています。事前に 0 に設定errnoし、後で毎回チェックすることもできますが、これは大変な作業であり、並行処理を開始するときには実行できない場合があります。

  4. エラー処理関数を呼び出します - エラー関数も上記の問題に対処する必要があるため、これは基本的にはバックを渡すだけですが、少なくとも独自のものを提供できます。また、R. が以下のコメントで指摘しているように、「エラーが発生した場合は常に終了する」などの非常に単純な場合を除き、単一のグローバル エラー処理関数に多くのことを要求しすぎて、発生する可能性のあるエラーを適切に処理できない可能性があります。プログラムが通常の実行を再開できる方法。多数のエラー処理関数を用意し、適切な関数を各ライブラリ関数に個別に渡すことは技術的には可能ですが、最適なソリューションとは言えません。このようにエラー処理関数を使用することは、並行性が存在する場合に正しく使用することが困難であるか、不可能でさえあります。

  5. エラーが発生した場合に関数によって変更される引数を渡します。技術的には可能ですが、これまでに作成されたすべてのライブラリ関数に、この目的のために追加のパラメーターを追加することは実際には望ましくありません。

  6. 例外をスローする - あなたの言語はこれを行うためにそれらをサポートする必要があり、不明確な構造やプログラムの流れ、より複雑なコードなど、あらゆる種類の関連する問題が伴います。一部の人々 (私はその 1 人ではありません) は、例外を倫理的に同等のものと見なしていますlongjmp()

考えられるすべての方法には欠点と利点がありますが、人類はまだライブラリ関数からエラーを報告する完璧な方法を発見していません。

于 2013-09-23T16:54:30.807 に答える