Code Fix

中級

strcmpの戻り値を真偽値と勘違いして条件が逆になる

strcmpは文字列が等しいときに0を返します。if (strcmp(a, b))と書くと「等しくないとき」に真になり、多くの人が期待する「等しいとき」とは逆の分岐になります。

なぜエラーが出ないのか

出力: no match
(エラーなし)
Cは何も報告しません。コンパイルも実行も正常に完了するためです
出力: no match
実際の挙動 — 期待した結果と食い違っている箇所
見つけ方
エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます

このエラーが出る典型パターン

パターン1

 1  #include <stdio.h>
 2  #include <string.h>
 3  
 4  int main(void) {
 5      char name[] = "Taro";
 6      if (strcmp(name, "Taro")     ) {
                        ^
 7          printf("match\n");
 8      } else {
 9          printf("no match\n");
10      }
11      return 0;
12  }
出力: no match期待: match

strcmpは文字列が等しいときに0を返します。0はCでは偽なので、if(strcmp(...))とだけ書くと「一致しない」ときに真になってしまいます。等しいか調べたいときは== 0を付けます。

直し方: strcmp(name, "Taro") を strcmp(name, "Taro") == 0 にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  #include <stdio.h>
 2  #include <string.h>
 3  
 4  int main(void) {
 5      char color[] = "red";
 6      if (strcmp(color, "red")     ) {
                        ^
 7          printf("is red\n");
 8      } else {
 9          printf("not red\n");
10      }
11      return 0;
12  }
出力: not red期待: is red

colorは実際に"red"と一致していますが、strcmpの戻り値をそのままifの条件にすると、一致(0)が偽と評価されるため逆の分岐に入ります。

直し方: strcmp(color, "red") を strcmp(color, "red") == 0 にします。

この問題を解いてみる →

パターン3

 1  #include <stdio.h>
 2  #include <string.h>
 3  
 4  int main(void) {
 5      char cmd[] = "quit";
 6      if (strcmp(cmd, "quit")     ) {
                        ^
 7          printf("quit\n");
 8      } else {
 9          printf("continue\n");
10      }
11      return 0;
12  }
出力: continue期待: quit

cmdは"quit"そのものなのに、strcmpの結果を裸のままifに渡すと、意図と正反対の分岐に入ってしまいます。

直し方: strcmp(cmd, "quit") を strcmp(cmd, "quit") == 0 にします。

この問題を解いてみる →

パターン4

 1  #include <stdio.h>
 2  #include <string.h>
 3  
 4  int main(void) {
 5      char status[] = "ok";
 6      if (strcmp(status, "ok")     ) {
                        ^
 7          printf("is ok\n");
 8      } else {
 9          printf("not ok\n");
10      }
11      return 0;
12  }
出力: not ok期待: is ok

strcmpの戻り値をそのままifに渡すと、一致(0)が偽と評価されるため逆の分岐に入ります。

直し方: strcmp(status, "ok") を strcmp(status, "ok") == 0 にします。

この問題を解いてみる →

パターン5

 1  #include <stdio.h>
 2  #include <string.h>
 3  
 4  int main(void) {
 5      char role[] = "admin";
 6      if (strcmp(role, "admin")     ) {
                         ^
 7          printf("is admin\n");
 8      } else {
 9          printf("not admin\n");
10      }
11      return 0;
12  }
出力: not admin期待: is admin

roleは実際に"admin"と一致していますが、== 0を付けないと逆の分岐に入ってしまいます。

直し方: strcmp(role, "admin") を strcmp(role, "admin") == 0 にします。

この問題を解いてみる →

よくある誤解

「trueっぽい名前の関数はtrueで成功を表す」という直感はここでは裏切られます。strcmpの0は「差が無い」という意味であり、Cでは0は偽と評価されるため、素直にif(strcmp(...))と書くと「文字列が違うときに実行される」コードになってしまいます。等しいかどうかを見たいときはif (strcmp(a, b) == 0)と書きます。

実務での勘所

strcmpが単純な真偽値ではなく数値を返すのは、等しいかどうかだけでなく、辞書順でどちらが前に来るかという順序の情報も同時に伝えるためです。負の値は最初の引数が辞書順で前、正の値は後、0は等しいことを意味します。この「負・0・正の3値を返す」という規約は、qsortに渡す比較関数やmemcmpなど、C標準ライブラリの比較系関数に共通するパターンです。strcmpの使い方を理解すると、他の比較関数の戻り値も同じ考え方で読めるようになります。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)