C言語で関数の引数のチェックは行うべきだと思う

  • このエントリーをはてなブックマークに追加
Pocket

C言語で関数を作るとき、引数のチェックはきちんと行っていますか?

面倒だからとか、こういう値は絶対にこないからといって、引数チェックの処理を省いていませんか?

C言語に限った話ではないですが、関数の引数チェックをしないというのは、かなり危険です。

そのことについて、考えをお話ししようと思います。

想定外の引数が来た時に、暴走する可能性がある

よくあるのが、関数の引数が想定していた値でないとか、ポインタがNULLであるとかで、その後の関数内の処理で、プログラムが暴走したり、強制終了したりする可能性がありあす。

次の例を見てください。

void func(int* a, int b)
{
    int i;
    int sum;

    sum = 0;
    for (i = 0; i < b; i++) {
        sum += a[i];
    }
    printf("データ合計\n");
    printf("sum = %d\n", sum);
}

ポインタで渡されたaを配列として扱い、要素数bまで、要素の合計を出力するというものです。

ここで、引数のポインタaがNULLだったら、どうなるでしょうか?

また、要素数bが実際の配列の要素数より大きい値だったらどうなるでしょうか?

おそらく、プログラムは強制終了されるでしょう。

こういう関数は、使い物になりません。なぜなら、想定外の値が渡されてくるということを、想定されていないからです。

つまり、NULLポインタではない、要素数を超える値ではないことが前提の関数ということになっているからです。

配列の要素数をMAX 10として定義して、関数を書き換えてみます。

#define MAX 10

void func(int* a, int b)
{
    int i;
    int sum;

    if ((a == NULL) || (b < 0) || (b >= MAX)) {
        printf("データエラー\n");
    } else {
        sum = 0;
        for (i = 0; i < b; i++) {
            sum += a[i];
        }
        printf("データ合計\n");
        printf("sum = %d\n", sum);
    }
}

どうでしょうか?

引数のポインタがNULLの場合、bの値が負の値かMAX(10)以上の場合、”データエラー”とメッセージを出すようにしました。

これで、どんな引数がきても、関数内でプログラムが強制終了することはないでしょう。

関数を使う側は、どんな使い方をするかわからない

関数を使う側は、関数の中身を理解しようとはしません。引数に何を渡して、何を戻してくれるかが、わかればいいからです。

したがって、使う側は引数の範囲など気にもしません。その値で関数が暴走するかどうかを、いちいち検討しません。

なので、関数を使う側で、どんな使われ方をしても、関数内でプログラムが暴走したり、強制終了したりしないようにしておく必要があるわけです。

先ほどの例で見てみましょう。

#include <stdio.h>

void func(int* a, int b)
{
    int i;
    int sum;

    sum = 0;
    for (i = 0; i < b; i++) {
        sum += a[i];
    }
    printf("データ合計\n");
    printf("sum = %d\n", sum);
}

void main(void)
{
    int data[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

    func(data, 20);
}

こうしてしまうと、配列dataは要素数が10にもかかわらず、20を渡してしまっているので、func内の変数iが10になったとき、どういう動作になるかわかりませんね。

使う側が、わざと20と書いたにしろ、手が滑って20と書いたにしても、プログラムはきちんと動作しません。

一方で、

#include <stdio.h>

#define MAX 10

void func(int* a, int b)
{
    int i;
    int sum;

    if ((a == NULL) || (b < 0) || (b >= MAX)) {
        printf("データエラー\n");
    } else {
        sum = 0;
        for (i = 0; i < b; i++) {
            sum += a[i];
        }
        printf("データ合計\n");
        printf("sum = %d\n", sum);
    }
}

void main(void)
{
    int data[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

    func(data, 20);
}

こちらの場合は、funcに引数のチェックがあるため、”データエラー”と表示されて、プログラムは正常終了します。

mainの処理は、全く同じに見えても、関数の書き方1つで、どうにでもなってしまうということが、おわかりいただけたと思います。

関数を作るときに、引数がある場合、こういう範囲内で実行させるというものがあると思うんですよ。

そうでなければ、プログラムが書けないはずですからね。

だから、引数をチェックすることは、難しい話ではないはずなんです。

そういうわけなので、引数のチェックが面倒とか、こういう値はこないだろうとか、思わずに、きちんと引数のチェックはしましょう。

そうすることで、あなたの作るプログラムが強くなっていきます。

引数のチェックをすることで、関数の信頼度があがる

引数のチェックをすることで、関数があらゆる値の引数に対して、処理がなされるため、関数としての信頼度があがります。

関数の信頼度があがるということは、それを作ったあなたのプログラムの信頼度があがるということです。

関数は作るだけでは意味がありません。使用されて、はじめて関数を作った意味があります。

信頼度の高いプログラムを作ってもらえると思えば、あなたのところに、プログラムを作ってほしいと依頼が来るようになります。

プログラムを作れば、ますます、あなたのスキルが磨かれて、よりよいプログラムができますし、プログラマーとしてもどんどん成長していくでしょう。

たかが、引数チェックですが、これだけで、関数の使いやすさに違いが出るのです。面倒なことをやれば、信頼度も、自分自身のスキルもあがるのです。

まとめ

C言語で関数を作るとき、引数のチェックをするべきであることの私の考えをお話しました。

それでも、「面倒だからいいや。」というのであれば、それでも構いません。

ただ、使いにくい関数が出来上がるだけです。

やっぱり、きちんと引数チェックをしようと思われたのであれば、すばらしいです。

そういう考えであれば、必ず、プログラマーとして成長できます!

  • このエントリーをはてなブックマークに追加

コメントを残す

*