Code Fix

中級

符号ありと符号なしの比較で -1 が巨大な数として扱われる

int型とunsigned int型を比較すると、int型の値は自動的にunsigned intへ変換されます。負の数は非常に大きな正の数として扱われ、直感に反する比較結果になります。

なぜエラーが出ないのか

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

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

パターン1

 1  #include <stdio.h>
 2  
 3  int main(void) {
 4      int a = -1;
 5      unsigned int b = 1;
 6      if (a < b              ) {
                     ^
 7          printf("a is less\n");
 8      } else {
 9          printf("a is not less\n");
10      }
11      return 0;
12  }
出力: a is not less期待: a is less

aとbを比較すると、int型のaは自動的にunsigned intへ変換されます。-1は非常に大きな正の数になるため、bより大きいと判定されてしまいます。bをintにキャストすれば直感通りの比較になります。

直し方: a < ba < (int)b にします。

この問題を解いてみる →

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

パターン2

 1  #include <stdio.h>
 2  
 3  int main(void) {
 4      int score = -5;
 5      unsigned int passLine = 3;
 6      if (score < passLine              ) {
                           ^
 7          printf("fail\n");
 8      } else {
 9          printf("pass\n");
10      }
11      return 0;
12  }
出力: pass期待: fail

scoreが明らかにpassLineより低いにもかかわらず、符号なしへの暗黙変換によって「合格」と誤判定されてしまいます。

直し方: score < passLinescore < (int)passLine にします。

この問題を解いてみる →

パターン3

 1  #include <stdio.h>
 2  
 3  int main(void) {
 4      int balance = -10;
 5      unsigned int fee = 5;
 6      if (balance < fee              ) {
                         ^
 7          printf("overdrawn\n");
 8      } else {
 9          printf("ok\n");
10      }
11      return 0;
12  }
出力: ok期待: overdrawn

残高がマイナスで手数料より明らかに少ないのに、符号なしへの変換によって「残高は十分」と誤判定されます。金額計算でunsignedを不用意に使うと危険な理由の一つです。

直し方: balance < feebalance < (int)fee にします。

この問題を解いてみる →

よくある誤解

「-1は明らかに1より小さい」という直感はここでは通用しません。Cの通常の算術変換規則により、符号ありと符号なしの比較では符号ありの値がunsignedに変換されるため、-1は0xFFFFFFFF(約42億)という巨大な値になり、1より大きいと判定されます。

まとめ

符号ありと符号なしの比較で -1 が巨大な数として扱われるは中級でつまずきやすい項目です。上の3パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。

演習をはじめる

関連するエラー

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