Cでの符号付きから符号なしへの変換 - 常に安全ですか?

短い答え

あなたの i 変換される UINT_MAX + 1 を追加して符号なし整数に の場合、加算は符号なしの値で実行され、大きな result になります。 (u の値に応じて と i ).

長文

C99 標準によると:

あなたの場合、unsigned int (u) が 1 つあります。 ) および signed int (i )。上記 (3) を参照すると、両方のオペランドが同じランクであるため、 i 変換する必要があります 符号なし整数に変換します。

ここで、上記の (2) を参照する必要があります。あなたの i UINT_MAX + 1 を追加することにより、符号なしの値に変換されます .したがって、結果はUINT_MAXの方法によって異なります 実装で定義されています。サイズは大きくなりますが、オーバーフローすることはありません。理由は次のとおりです:

ボーナス:算術変換セミ WTF

#include <stdio.h>

int main(void)
{
  unsigned int plus_one = 1;
  int minus_one = -1;

  if(plus_one < minus_one)
    printf("1 < -1");
  else
    printf("boring");

  return 0;
}

このリンクを使用してオンラインで試すことができます:https://repl.it/repls/QuickWhimsicalBytes

ボーナス:算術変換の副作用

算術変換規則を使用して、UINT_MAX の値を取得できます 符号なしの値を -1 に初期化する 、つまり:

unsigned int umax = -1; // umax set to UINT_MAX

上記の変換規則により、システムの符号付き数値表現に関係なく、これは移植可能であることが保証されています。詳細については、この SO の質問を参照してください:Is it safe to use -1 to set all bits to true?


署名付きから署名なしへの変換はできません 必然的に、署名された値の表現をコピーまたは再解釈するだけです。 C 標準 (C99 6.3.1.3) の引用:

最近ほぼ普遍的な 2 の補数表現の場合、ルールはビットの再解釈に対応します。ただし、他の表現 (符号と大きさまたは 1 の補数) の場合、C の実装では同じ結果が得られるように調整する必要があります。つまり、変換でビットをコピーすることはできません。たとえば、表現に関係なく、(unsigned)-1 ==UINT_MAX です。

一般に、C での変換は、表現ではなく値を操作するように定義されています。

元の質問に答えるには:

unsigned int u = 1234;
int i = -5678;

unsigned int result = u + i;

i の値は unsigned int に変換され、UINT_MAX + 1 - 5678 になります。 .この値は、符号なしの値 1234 に加算され、UINT_MAX + 1 - 4444 になります。 .

(符号なしオーバーフローとは異なり、符号付きオーバーフローは未定義の動作を引き起こします。ラップアラウンドは一般的ですが、C 標準では保証されていません。また、コンパイラの最適化によって、不当な仮定を行うコードが混乱する可能性があります。)


聖書を参照:

  • 加算演算により、int が unsigned int に変換されます。
  • 2 の補数表現と同じサイズの型を想定すると、ビット パターンは変わりません。
  • unsigned int から signed int への変換は実装に依存します。 (しかし、最近のほとんどのプラットフォームでは、おそらく期待どおりに機能します。)
  • 異なるサイズの符号付きと符号なしを組み合わせる場合、ルールはもう少し複雑になります。