...ing logging 4.0

はてなブログに移行しました。D言語の話とかいろいろ。

assert (3)

さて,Assertion の説明も最終回となりました.
今回は,唯一気をつけなければいけないことを説明しようと思います.


既に assert は,NDEBUG マクロを定義するとなかったことにされることは説明しました.
それでは,次のようなコードはどうなるでしょうか?
ポインタが出てくるのでがんばってください(^^;

// #define NDEBUG
#include <assert.h>
#include <stdio.h>
 
enum tagBOOL { true = -1, false = 0 };
typedef  enum tagBOOL  bool;
 
bool checked_swap(int* a, int* b)
{
  int tmp;
  if( a == NULL || b == NULL )
    {
      return false;
    }
  tmp = *a;
  *a = *b;
  *b = tmp;
  return true;
};
 
int main(int argv, char* argc[])
{
  int a = 1, b = 2, *pa, *pb;
  pa = &a;
  pb = &b;
  printf("a=%d, b=%d\n", a, b);
  assert( checked_swap( pa, pb) == true );
  printf("a=%d, b=%d\n", a, b);
  return 0;
}

ここで

enum tagBOOL { true = -1, false = 0 };
typedef  enum tagBOOL  bool;

というのは,C言語には bool 型がないので(C99にはあるみたい?)C++風に使えるように自分で勝手に定義しているだけです.


checked_swap 関数は,二つの int 型ポインタを引数に取り,2つのポインタの中身を入れ替える関数です.
ただし,ポインタが NULL のときは関数が false (偽)を返して処理が失敗したことを伝えます.
逆に成功したときは true を返します.


さて,次がポイントです.
assert( checked_swap( pa, pb) == true );
これは,checked_swap 関数が常に真であることを表明しています.


このままコンパイルして実行するとこうなります.

>gcc assert.c
>assert
a=1, b=2
a=2, b=1

プログラム自体はコンパイルできるし,処理にも一見おかしいところは見当たりません.
それでは,最初のコメントアウトをはずしてコンパイル,実行してみましょう.

>gcc assert.c
>assert
a=1, b=2
a=1, b=2

よく見てみると・・・ checked_swap 関数が動いていません.
もうお分かりですね?
NDEBUG マクロを定義したおかげで assert が何もしない命令に置き換えられたため,assert の条件部分に書いた checked_swap 関数が呼び出されなくなってしまったのです.
従って,上記のプログラムの main 関数を正しく書き直すとこうなります.

int main(int argv, char* argc[])
{
  int a = 1, b = 2, *pa, *pb;
  pa = &a;
  pb = &b;
  printf("a=%d, b=%d\n", a, b);
  bool swap_was_success = checked_swap( pa, pb);
  assert( swap_was_success == true );
  printf("a=%d, b=%d\n", a, b);
  return 0;
}

これで,デバッグモードでもリリースモードでも共通のコードが使えるようになりました.
めでたしめでたし(^^


これを機会にどんどん assert を使っていきましょう.
ではでは.