答え
else
は「1ではないとき」に実行されるので、「2のとき」というコメントはおかしい!
if (n == 1) {
/* 1のとき */
puts( "one" );
} else {
/* 2のとき */
puts( "two" );
}
if
文なのでコメントを削除してスッキリさせつつ……
if (n == 1) {
puts( "one" );
} else {
puts( "two" );
}
/*
* 引数が1のときは"one"、2のときは"two"と表示する。
* それ以外のときの動作は未定義。
*/
void PrintOneOrTwo(int n) {
assert((n == 1) || (n == 2));
if (n == 1) {
puts( "one" );
} else {
puts( "two" );
}
}
解説
これは、コメントの書き方に関する出題でした。問題の関数では、if
文のコメントに少しおかしなところがあります。
void PrintOneOrTwo(int n) {
if (n == 1) {
/* 1のとき */
puts( "one" );
} else {
/* 2のとき */
puts( "two" );
}
}
「2のとき」というコメントがありますが、これが実際の動作と一致していないのが分かるでしょうか。
(n == 1)
という条件に対するelse
だから……
void PrintOneOrTwo(int n) {
if (n == 1) {
/* 1のとき */
puts( "one" );
} else {
/* 1ではないとき */
puts( "two" );
}
}
if
文の説明みたいになってしまったわね。ここは思い切って、コメント自体をなくしてみるのはどう?
void PrintOneOrTwo(int n) {
if (n == 1) {
puts( "one" );
} else {
puts( "two" );
}
}
if
文なら、むしろコメントを書かないほうが読みやすいんじゃないかしら。
else
のところにif
を追加すれば……
/* 引数が1のときは"one"、2のときは"two"と表示する */
void PrintOneOrTwo(int n) {
if (n == 1) {
puts( "one" );
} else if (n == 2) {
puts( "two" );
}
}
さて。いつか1や2以外の値を追加したくなるかもしれないというのは、この関数が今のところ「1または2」しか受け付けないからですね。これは、関数に「定義域」が定められていることを意味します。定義域とは、引数として与えられる値の範囲のことです。
定義域の考え方は、実は数学で習う関数と大きく変わりません。
では、問題の関数について確認してみましょう。
/* 引数が1のときは"one"、2のときは"two"と表示する */
void PrintOneOrTwo(int n) {
……
}
コメントされている仕様から、この関数の定義域は「1または2」だと分かります。そして、main()
からは、次のように呼び出されています。
PrintOneOrTwo(1);
PrintOneOrTwo(2);
引数の値が定義域に収まっているので、期待どおりに動作しているということです。
もし、これが数学の関数なら、「1または2」以外の値を与えることはできません。でも実際には、「1または2」以外の値を引数にして関数を呼び出すプログラムを書くこともできますね。では、この関数の引数に3
という値を与えたらどうなるでしょうか?
今回の関数では、「1または2」以外の値を受け取ったときの動作が決められていませんでした。そのため、引数に3
と書いた場合にどのような動作をするかは、「不明である」というのが仕様なのです。このような動作を「未定義(undefined)」といいます。
3
を指定するのは無理ってことになりませんか?
/*
* 引数が1のときは"one"、2のときは"two"と表示する。
* それ以外のときの動作は未定義。
*/
void PrintOneOrTwo(int n) {
……
}
3
を指定してはいけないっていうことも分かるわね。
ちなみに。
関数の使い方をしっかりコメントに書いていたとしても、間違いを100%防げるわけではありません。今回の関数を、うっかり引数を3
にして呼び出してしまうこともあるでしょう。
そこで、次のようにassertを入れておくのがおすすめです。こうすれば「1または2」以外の値が明確に禁止されるので、早い段階で間違いに気付けるようになるでしょう。
#include <assert.h>
void PrintOneOrTwo(int n) {
assert((n == 1) || (n == 2)); /* ← 「1または2」以外の値を明確に禁止! */
……
}
#include <assert.h>
#include <stdbool.h>
void PrintOneOrTwo(int n) {
if (n == 1) {
puts( "one" );
} else if (n == 2) {
puts( "two" );
} else {
assert(false); /* ← 「1または2」以外の値を明確に禁止! */
}
}
switch
文で書き換えてもOKよ!
#include <assert.h>
#include <stdbool.h>
void PrintOneOrTwo(int n) {
switch (n) {
case 1:
puts( "one" );
break;
case 2:
puts( "two" );
break;
default:
assert(false); /* ← 「1または2」以外の値を明確に禁止! */
}
}
修正後のプログラム
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
/*
* 引数が1のときは"one"、2のときは"two"と表示する。
* それ以外のときの動作は未定義。
*/
void PrintOneOrTwo(int n) {
assert((n == 1) || (n == 2));
if (n == 1) {
puts( "one" );
} else {
puts( "two" );
}
}
int main(void) {
PrintOneOrTwo(1);
PrintOneOrTwo(2);
return EXIT_SUCCESS;
}
one
two