Cでは、自動型変換は「通常の算術変換」の原則に基づいています。符号付きと符号なしの異なる数値が式に含まれる場合、次のルールに従って変換が行われます:
危険な算術の例:
int a = -1; // 符号付き unsigned int b = 1; printf("%d\n", a < b); // いつもfalse、なぜならaは非常に大きな符号なしに変換される
結果:-1は符号なしに変換されると非常に大きな正の数になります。
覚えておくべきこと:
質問: 式(int)(unsigned)-1はどのような結果を返しますか?
期待される間違った答え: "-1、なぜなら-1はintに変換されるから。"
正しい答え:
式(unsigned)-1では、まず-1が符号なしに変換されます(32ビットプラットフォームでは0xFFFFFFFFになります)、その後、再び符号付きintに戻されるため、実装に依存しますが、多くの場合再び-1になります(2の補数を使用している場合)。ただし、より正確に言うと:結果は符号付き数の表現標準に依存しますが、大多数の実装では-1になるでしょう。
例:
int x = (int)(unsigned)-1; // x == -1(ほとんどのプラットフォームで)
物語
文字列のハンドラではサイズの比較関数を使用しました:文字列の長さが負である可能性がある場合、プログラムはエラーを報告していました。しかし、長さはsize_t(符号なし)型であり、コード
if(length < 0)の比較は常にfalseを返すため、無限ループとメモリオーバーフローを引き起こしました。
物語
プロトコルのパース時に、ネットワークパケットには符号なしのフィールドが含まれており、ローカル変数は符号付きでした。いくつかの値を処理する際の符号なしのオーバーフローにより、パケットの長さの計算が誤って行われ、バッファーオーバーフローの脆弱性が生じました。
物語
ログの日付比較モジュールは日付を符号なしintとして保持していましたが、日付の範囲をintで検索していました。いくつかの境界値が期待された例外の発生ではなく、レコードの不正フィルタリングと重要なログの喪失を引き起こしました。