第10問の答え

答え

コマンドライン引数が必ず与えられる前提になっているのが間違い!
  if (strstr(argv[1], text) != 0) {
    ……
コマンドライン引数の数をチェックするのが正解!
  if (
    (argc >= 2) &&
    (strstr(argv[1], text) != 0)
  ) {
    ……

解説

このプログラムの問題点は、コマンドライン引数の数をチェックしていないことでした。最初のif文で、いきなりargv[1]にアクセスしていますね。

  if (strstr(argv[1], text) != 0) {
    ……

1つ目のコマンドライン引数が必ずあると思って処理してしまっているため、コマンドライン引数が1つも与えられなかったときにクラッシュしてしまいます。

修正の方法はいくつか考えられますが、例えばこのようにすればクラッシュを避けられるでしょう。

  if (
    (argc >= 2) &&
    (strstr(argv[1], text) != 0)
  ) {
    ……

コマンドライン引数が1つはあることをargcで確認してから、argv[1]にアクセスするよう手直ししました。

これって、ケアレスミスですよね?
そうね。
こういうミスを防ぐ方法って何かあるんですか?
それは難しいんじゃないかしら。人間だもの。
ええっ!じゃあ、どうすれば……。
ミスしたところがないか、確認する時間をつくるのがいいわね。

プログラミングをする以上、完全にミスを防ぐことはできません。今回の問題は単純なミスですが、何かに気を取られて同じような間違え方をしてしまうこともあるでしょう。それは、しかたのないことです。ミスしないように頑張るよりも、あとからミスに気付くことができる方法を考えるほうが生産的かもしれません。

例えば、コードレビューの時間をとる方法があります。プログラムを作ったら、「期待どおりに動かないケースはないかな」と考えてみましょう。100%とはいかないまでも、ある程度のミスを早期発見できるはずです。

ここがポイント!
プログラムにミスがないか、確認する時間をとりましょう!

修正後のプログラム

main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
  char text[] = "hello";

  if (
    (argc >= 2) &&
    (strstr(argv[1], text) != 0)
  ) {
    printf("You said %s.\n", argv[1]);

    return EXIT_SUCCESS;

  } else {
    printf("Say %s.\n", text);

    return EXIT_FAILURE;
  }
}
実行結果
↓コマンドライン引数に「hello」が含まれる場合(「hello-world」など):
You said hello-world.

↓コマンドライン引数に「hello」が含まれない場合:
Say hello.