符号ありと符号なしの比較で -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 < b を a < (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 < passLine を score < (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 < fee を balance < (int)fee にします。
よくある誤解
「-1は明らかに1より小さい」という直感はここでは通用しません。Cの通常の算術変換規則により、符号ありと符号なしの比較では符号ありの値がunsignedに変換されるため、-1は0xFFFFFFFF(約42億)という巨大な値になり、1より大きいと判定されます。
まとめ
符号ありと符号なしの比較で -1 が巨大な数として扱われるは中級でつまずきやすい項目です。上の3パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。