答え

関数名
SearchUpper()
から連想される「大文字を検索する」という動作が実際と違う!
int SearchUpper(const char *pText) {
……

「大文字が含まれるか」を調べる関数なので、それが分かる名前(命名法)にするのがベター!
int ContainsUpper(const char *pText) {
……

戻り値を
bool
にすると、さらに分かりやすい!
bool ContainsUpper(const char *pText) {
……
解説
もしも、次のような関数があったとしたら、どう思うでしょうか?
int Add(int n1, int n2) {
return n1 - n2;
}

これは……さすがにダメですよ!

そうね。どうダメなのか説明できる?

はい。関数名の
Add()
は「足す」という意味なのに、実際には引き算になってます。

そのとおり!名前と動作がズレているのは困るわね。
この例は、今回のプログラムの問題点をストレートに示しています。関数名から連想される動作と、実際の動作との間に食い違いがあるのです。
とくに困るのは、名前と動作のどちらが間違っているのか分からなくなってしまったときです。プログラムを作った直後なら正しく直せますが、時間が経って「あのときは何をしたかったんだっけ?」と忘れてしまうこともあるでしょう。できる限り最初から、実態に合う名前を付けておくことが大切です。
では、今回のプログラムを確認してみましょう。
int SearchUpper(const char *pText) {
const char *p = pText;
while (*p != '\0') {
if (isupper(*p)) {
return 1;
}
p += 1;
}
return 0;
}
関数名SearchUpper()
から連想されるのは、「大文字を検索する」という動作です。戻り値がint
なので、「最初に見つけた大文字の文字コードが返ってくるのかな」と想像する人が少なくないでしょう。
ところが、実際の動作は次ようなものでした。
- 文字列中に大文字が1つでもあれば、1を返す
- 文字列中に大文字が1つもなければ、0を返す
「大文字を検索する」というよりは、「大文字が含まれるか」を調べる動作になっていますね。さて、間違っているのは関数名でしょうか?それとも動作のほうでしょうか?
今回作ったのは、「文字列に大文字が含まれているかどうか確認するプログラム」でした。そして、実行結果は期待どおりでした。ということは、直すべきなのは関数名のほうですね。
例えば、次のような関数名に変更すれば、動作との食い違いをなくせるでしょう。
int ContainsUpper(const char *pText) {
……
}

たしかに……さっきまでのモヤモヤは晴れたような気がします。

でしょう?これって意外に重要なとこよ。

そうなんですか?

そうなのよ。今回みたいに微妙にズレた名前がたくさん出てくるプログラムがあったら、どうなると思う?

うえぇ……それは勘弁してほしいです!

ね?だから全部を完璧にできなくても、できる限り正しい名前を付けることが大切なの。

なるほど。「できる限り」だったらやれそうです!
ちなみに、今回のプログラムでは戻り値をbool
にするのもおすすめです。bool
はC++で標準的に使えるようになった型ですが、実はC言語でも<stdbool.h>
をインクルードすれば使えます。
#include <stdbool.h>
bool ContainsUpper(const char *pText) {
……
}
関数名がContainsUpper()
で戻り値がbool
なら、「大文字が含まれていた場合にtrue
を返すんだな」と想像しやすくなりますね。
ここがポイント!

関数には、実際の動作を正しく想像できそうな名前を付けよう!
修正後のプログラム
main.c
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <ctype.h>
bool ContainsUpper(const char *pText) {
const char *p = pText;
while (*p != '\0') {
if (isupper(*p)) {
return true;
}
p += 1;
}
return false;
}
int main(void) {
const char *pName = "Leo";
if (ContainsUpper(pName)) {
printf("%s contains uppercase.\n", pName);
}
return EXIT_SUCCESS;
}
実行結果
Leo contains uppercase.