第17章 if...

 もし、こういうときにはこうコンパイルして、こういうときにはまたこうコンパイルして...。そういう風にコントロールできると便利なことがあります。今回はそういう話です。


 では、今回の要点です。


 では、いってみましょう。


 さて、早速次のプログラムを見て下さい。

プログラム実行結果
// ShrpIf1.cpp
#include <iostream.h>
#include <process.h>

#define numof(array)  (sizeof (array) / sizeof *(array))

int Average(const int* const pnValues, const int nNumOf)
{
    if(pnValues == NULL || nNumOf < 0)
    {
        printf("Average 関数に変な引数が渡されました!\n");
        exit(1);
    }

    int i, nSum;

    for(i = nSum = 0; i < nNumOf; i++)
        nSum += pnValues[i];
    return nSum;
}

int main()
{
    int nValues[] = {
        12, 56, 23, 93, 84, 45, 77, 87, 100, 45,
    };

    cout << "平均値 : "
         << Average(nValues, numof(nValues)) << endl;

    return 0;
}
平均値 : 62

 平均値を求めるだけのプログラムです。

 しかしこの Average という関数。こんなものがついています。

if(pnValues == NULL || nNumOf < 0)
    exit(1);

 どうやら、引数に異常がないか確かめているようです。異常がある時に、エラーメッセージを表示し、強制終了関数 exit を呼ぶようにしていますね。

 しかし、平均を求める関数くらいでいちいち引数を確かめたくはないものです。エラー処理はいろいろするべきではありますが、普通に使っていれば何の問題もないところまでエラー処理は書きたくありません。

 とはいえ、プログラムにバグのある内は、ひょっとしてこのコードのおかげでいろいろ助かるかもしれません。

 ということで、一応プログラムの動作を確かめている最中(デバッグ中)ではこのようにしておき、バグの取れたことが確認できたらこの判定を取り除くといいわけです。また利用するかもしれないので、コメントアウトするとしましょう。

 しかし、まだ問題があります。ここ以外にも同じようなことをしているとき、これらコードを全部コメントアウトして回るのは面倒です。そんなことをやっていては、日が暮れるというものです。何か、便利な方法はないのでしょうか?


 そこで、第1部第70章で使った #if ディレクティブが役に立ちます。

 では、見てみましょう。

プログラム
// ShrpIf1b.cpp
#include <iostream.h>
#include <process.h>

#define ONDEBUG

#define numof(array)  (sizeof (array) / sizeof *(array))

int Average(const int* const pnValues, const int nNumOf)
{
#if defined(ONDEBUG)
    if(pnValues == NULL || nNumOf < 0)
    {
        printf("Average 関数に変な引数が渡されました!\n");
        exit(1);
    }
#endif

    int i, nSum;

    for(i = nSum = 0; i < nNumOf; i++)
        nSum += pnValues[i];
    return nSum;
}

int main()
{
    int nValues[] = {
        12, 56, 23, 93, 84, 45, 77, 87, 100, 45,
    };

    cout << "平均値 : "
         << Average(nValues, numof(nValues)) << endl;

    return 0;
}

 この部分に注目して下さい。

#define ONDEBUG

 何と、ONDEBUG の後に何も書いてありません。ということは、このマクロは ONDEBUG と書いても消えてなくなるだけのマクロです。

 「これは無意味な #define なのでは?」と思われるかもしれませんが、ここではマクロの「名前を定義する」という部分だけを利用します。

 で、次はここです。

#if defined(ONDEBUG)
    if(pnValues == NULL || nNumOf < 0)
    {
        printf("Average 関数に変な引数が渡されました!\n");
        exit(1);
    }
#endif

 引数の確認部分が、何やら囲まれています。if と書かれていますが、頭に # がついています。

 これは、「#if の後の評価式が真の時に、#endif までをコンパイルせよ」という命令です。# が先頭に付いている命令はプリプロセッサディレクティブ(プリプロセッサ指令)と呼ばれ、コンパイルする前にいろいろと操作する命令です。

 つまり、#if の後の評価式が偽の時はコンパイラは #endif までを認識しません。コンパイラに見られないように隠してしまうわけです。

 で、評価式はこれです。

defined(ONDEBUG)

 この defined というのは #if だけで使われる特別な命令で、引数の名前が定義されていれば真、定義されていなければ偽を返します。

 まとめると「もし ONDEBUG が定義されていたら #if の下から #endif の上までコンパイルしてね」ということになります。上のプログラムでは ONDEBUG が定義されているので、#if から #endif まではコンパイルされます。

 では、デバッグも済み、この引数の評価の部分を消したいと思います。どうすればいいでしょうか?

 そうです。こうすればいいのです。

// #define ONDEBUG

 こういった引数の評価をいくつしようとも、ONDEBUG の定義文をコメントアウトするだけで全てが消え去ってしまうのです。

 イメージとしてはこんな感じでしょうか。

プログラム
// ShrpIf1b.cpp
#include <iostream.h>
#include <process.h>

// #define ONDEBUG

#define numof(array)  (sizeof (array) / sizeof *(array))

int Average(const int* const pnValues, const int nNumOf)
{
#if defined(ONDEBUG)
    if(pnValues == NULL || nNumOf < 0)
    {
        printf("Average 関数に変な引数が渡されました!\n");
        exit(1);
    }
#endif

    int i, nSum;

    for(i = nSum = 0; i < nNumOf; i++)
        nSum += pnValues[i];
    return nSum;
}

int main()
{
    int nValues[] = {
        12, 56, 23, 93, 84, 45, 77, 87, 100, 45,
    };

    cout << "平均値 : "
         << Average(nValues, numof(nValues)) << endl;

    return 0;
}

 確かにコードは書いてあるのですが、コンパイラには見えないのです。

 またこの評価をしたいと思ったときはコメントアウトを消せばいいだけです。

#define ONDEBUG

 簡単ですね。


 長くなるので、今回はこれで終わりです。では、要点です。


 次回も #if ディレクティブについてのお話です。#if も多いと読みづらいプログラムになります。多少止むを得ない場合もあるのですが、うまく使えば読みにくさを軽減することもできます。工夫次第ではプログラムをとても柔軟にしてくれるでしょう。

 それでは、次回まで。


第16章 仰山の引数 | 第18章 if...2

Last update was done on 2000.8.15

この講座の著作権はロベールが保有しています