回答:
短い答え
あなたがi
される変換追加することによって、符号なし整数にUINT_MAX + 1
次に添加が大で、その結果、符号なしの値を用いて行われる、result
(の値に依存u
し、i
)。
長い答え
C99標準によると:
6.3.1.8通常の算術変換
- 両方のオペランドが同じタイプの場合、それ以上の変換は必要ありません。
- それ以外の場合、両方のオペランドが符号付き整数型または両方が符号なし整数型を持つ場合、整数変換ランクの小さいタイプのオペランドは、ランクの大きいオペランドのタイプに変換されます。
- それ以外の場合、符号なし整数型のオペランドのランクが他のオペランドの型のランク以上である場合、符号付き整数型のオペランドは、符号なし整数型のオペランドの型に変換されます。
- それ以外の場合、符号付き整数型のオペランドの型が符号なし整数型のオペランドの型のすべての値を表すことができる場合、符号なし整数型のオペランドは符号付き整数型のオペランドの型に変換されます。
- それ以外の場合、両方のオペランドは、符号付き整数型のオペランドの型に対応する符号なし整数型に変換されます。
あなたの場合、1つのunsigned int(u
)とsigned int(i
)があります。上記の(3)を参照すると、両方のオペランドのランクが同じであるため、符号なし整数に変換i
する必要があります。
6.3.1.3符号付きおよび符号なし整数
- 整数型の値が_Bool以外の別の整数型に変換されるとき、値が新しい型で表現できる場合、値は変更されません。
- それ以外の場合、新しいタイプが符号なしの場合、値が新しいタイプの範囲内になるまで、新しいタイプで表すことができる最大値よりも1だけ多く加算または減算することによって値が変換されます。
- そうでない場合、新しいタイプは署名され、値をそのタイプで表すことができません。結果は実装定義であるか、実装定義の信号が発生します。
ここで、上記の(2)を参照する必要があります。をi
追加すると、は符号なしの値に変換されますUINT_MAX + 1
。したがって、結果はUINT_MAX
実装での定義方法に依存します。大きくなりますが、次の理由でオーバーフローしません。
6.2.5(9)
結果の符号なし整数型で表すことができない結果は、結果の型で表すことができる最大値より1大きい数を法として減じられるため、符号なしオペランドを含む計算はオーバーフローすることはありません。
ボーナス:算術変換Semi-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の質問を参照してください:-1を使用してすべてのビットをtrueに設定しても安全ですか?
符号付きから符号なしへの変換は、必ずしも符号付き値の表現をコピーまたは再解釈するだけではありません。C標準(C99 6.3.1.3)の引用:
整数型の値が_Bool以外の別の整数型に変換されるとき、値が新しい型で表現できる場合、値は変更されません。
それ以外の場合、新しいタイプが符号なしの場合、値が新しいタイプの範囲内になるまで、新しいタイプで表すことができる最大値よりも1だけ多く加算または減算することによって値が変換されます。
そうでない場合、新しいタイプは署名され、値をそのタイプで表すことができません。結果は実装定義であるか、実装定義の信号が発生します。
最近ではほとんど普遍的な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標準では保証されていません-コンパイラーの最適化は、不当な仮定を行うコードに大混乱をもたらす可能性があります。)
1つの符号なし変数と1つの符号付き変数(または任意のバイナリ演算)が追加されると、両方が暗黙的に符号なしに変換されます。この場合、結果は非常に大きくなります。
したがって、結果は巨大で間違っている可能性があるという意味では安全ですが、クラッシュすることはありません。
以前に回答されたように、問題なく署名済みと署名なしの間でキャストできます。符号付き整数のボーダーケースは-1(0xFFFFFFFF)です。そこから足し算と引き算を試してみてください。そうすれば、キャストバックして正確にすることができます。
ただし、前後にキャストする場合は、変数のタイプが明確になるように変数に名前を付けることを強くお勧めします。例:
int iValue, iResult;
unsigned int uValue, uResult;
ヒントなしで名前が付けられている場合、より重要な問題に気を取られて、どの変数がどのタイプであるかを忘れるのは非常に簡単です。符号なしにキャストして、それを配列インデックスとして使用したくない場合。
ここでどのような暗黙の変換が行われているか、
iは符号なし整数に変換されます。
そして、このコードはuとiのすべての値に対して安全ですか?
明確に定義されているという意味で安全です(https://stackoverflow.com/a/50632/5083516を参照)。
ルールは通常読みにくい標準で書かれていますが、基本的に、符号付き整数で使用された表現はすべて、符号なし整数には2の補数表現の数値が含まれます。
加算、減算、乗算はこれらの数値で正しく機能し、「実際の結果」を表す2の補数を含む別の符号なし整数になります。
除算してより大きな符号なし整数型にキャストすると、明確な結果が得られますが、これらの結果は「実際の結果」の2の補数表現にはなりません。
(安全です。この例の結果が巨大な正の数値にオーバーフローしても、それをintにキャストして実際の結果を得ることができるという意味です。)
符号付きから符号なしへの変換は標準で定義されていますが、逆は実装定義であり、gccとmsvcの両方が変換を定義して、符号なし整数に格納された2の補数を符号付き整数に変換するときに「実際の結果」が得られるようにします。符号付き整数に2の補数を使用しない不明瞭なシステムでのみ他の動作が見つかることを期待しています。
https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation https://msdn.microsoft.com/en-us/library/0eex498h.aspx
恐ろしい答えが豊富
オズグルオズシタク
符号付きから符号なし(またはその逆)にキャストしても、数値の内部表現は変わりません。変更点は、コンパイラーが符号ビットを解釈する方法です。
これは完全に間違っています。
マット・フレドリクソン
1つの符号なし変数と1つの符号付き変数(または任意のバイナリ演算)が追加されると、両方が暗黙的に符号なしに変換されます。この場合、結果は非常に大きくなります。
これも間違っています。符号なしのintは、符号なしの型のビットにパディングを行うことにより、精度が等しい場合、intに昇格される場合があります。
smh
追加操作により、intはunsigned intに変換されます。
違う。多分それはそうかもしれませんし、そうでないかもしれません。
unsigned intからsigned intへの変換は実装に依存します。(しかし、最近はほとんどのプラットフォームで期待どおりに動作します。)
違う。オーバーフローが発生するか、値が保持される場合、これは未定義の動作です。
匿名の
iの値はunsigned int ...に変換されます。
違う。unsigned intに対するintの精度に依存します。
テイラー・プライス
以前に回答されたように、問題なく署名済みと署名なしの間でキャストできます。
違う。符号付き整数の範囲外の値を格納しようとすると、未定義の動作が発生します。
ようやく質問に答えることができます。
intの精度がunsigned intと等しい場合、uはsigned intに昇格され、式(u + i)から値-4444が取得されます。ここで、uとiに他の値がある場合、オーバーフローや未定義の動作が発生する可能性がありますが、これらの正確な数値では-4444 [1]になります。この値の型はintです。しかし、その値をunsigned intに格納して、unsigned intにキャストしようとすると、結果の値は(UINT_MAX + 1)-4444になります。
unsigned intの精度がintの精度より大きい場合、signed intはunsigned intに昇格され、値(UINT_MAX + 1)-5678が生成されます。これは、他のunsigned int 1234に追加されます。uとiは式を{0..UINT_MAX}の範囲外にする他の値。値(UINT_MAX + 1)は、結果が{0..UINT_MAX)の範囲内に収まるまで加算または減算され、未定義の動作は発生しません。 。
精度とは?
整数には、パディングビット、符号ビット、および値ビットがあります。符号なし整数には、明らかに符号ビットはありません。符号なしcharには、パディングビットがないことがさらに保証されます。整数のビット値の数は、整数の精度です。
[ゴチャ]
埋め込みビットがある場合、整数sizeofマクロだけでは整数の精度を決定できません。また、バイトのサイズは、C99で定義されているオクテット(8ビット)である必要はありません。
[1]オーバーフローは2つのポイントのいずれかで発生する可能性があります。追加の前(プロモーション中)-intに収まらない大きさのunsigned intがある場合。unsigned intがintの範囲内であったとしても、加算後にオーバーフローが発生する可能性があります。加算後も結果はオーバーフローする可能性があります。
int
されるunsigned int
ときにに変換されることを保証します。
int
またはunsigned int
タイプの何か、それらの種類の一つにunsigned int
またはがint
期待されているが。「又は等しい」変換ランクの列挙型が等しい可能にするTC2に加えint
、またはunsigned int
これらのタイプのいずれかに変換します。説明されているプロモーションがunsigned int
との間で変換されることは意図されていませんでしたint
。との間の一般的な型の決定はunsigned int
、int
TC2以降でも6.3.1.8によって管理されます。