ProgrammingCシステムプログラマ

Cにおける符号付き/符号なしの異なる数値間の型変換の特性を説明してください。どのような落とし穴があるのか、符号付き/符号なしの型の算術演算や比較において予期せぬ結果を避ける方法、そしてそれがプログラムの移植性にどのように影響するかについても説明してください。

Hintsage AIアシスタントで面接を突破

回答

Cでは、自動型変換は「通常の算術変換」の原則に基づいています。符号付きと符号なしの異なる数値が式に含まれる場合、次のルールに従って変換が行われます:

  • いずれかのオペランドが符号なしで、もう一方が符号付きの場合、符号付きの値は自動的に符号なしに変換されます。
  • これは、特に比較や算術操作を行った場合に予期しないオーバーフローを引き起こすことがあります。
  • 型のサイズも影響します:符号なしがよりビット幅が大きい場合、符号付きが符号なしに変換されます。

危険な算術の例:

int a = -1; // 符号付き unsigned int b = 1; printf("%d\n", a < b); // いつもfalse、なぜならaは非常に大きな符号なしに変換される

結果:-1は符号なしに変換されると非常に大きな正の数になります。

覚えておくべきこと:

  • 符号に混乱がある場合は常に明示的に型を変換する。
  • 型のサイズ(int、long、uint32_tなど)に注意して、変換が予測可能に行われるようにする。
  • 特に境界チェックや算術において、符号付きと符号なしの変数を別々のロジックで扱う。

ひっかけ質問

質問: 式(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で検索していました。いくつかの境界値が期待された例外の発生ではなく、レコードの不正フィルタリングと重要なログの喪失を引き起こしました。