7

C を使用して short と int の間でキャストするときに混乱します。short は 16 ビット、int は 32 ビットと仮定します。以下のコードでテストしました:

unsigned short a = 0xFFFF;
signed short b = 0xFFFF;

unsigned int u16tou32 = a;
unsigned int s16tou32 = b;
signed int u16tos32 = a;
signed int s16tos32 = b;

printf("%u %u %d %d\n", u16tou32, s16tou32, u16tou32, s16tou32);

私が得たものは次のとおりです。

  • u16tou32:65535
  • s16tou32:4294967295
  • u16tos32:65535
  • s16tos32: -1

私が混乱しているのは、s16 から u32 への変換と、u16 から s32 への変換です。s16 から u32 は「符号拡張」を行っているようですが、u16 から s32 はそうではありません。この背後にあるルールは正確には何ですか?また、これは実装依存ですか? このタイプのキャストを C で行うのは安全ですか? それとも、予期しない結果を避けるために自分でビット操作を使用する必要がありますか?

4

4 に答える 4

4

ここで起こっていることは、引数の右側が最初に 16 ビットから 32 ビットに拡張され、左側の型への変換は代入時にのみ発生することです。これは、右辺が符号付きの場合は 32 ビットに変換されるときに符号拡張され、同様に符号なしの場合はゼロが埋められることを意味します。

キャストに気をつけていれば問題はないはずですが、非常にパフォーマンスを重視するようなことをしているのでない限り、余分なビット演算が何回か発生しても問題はありません。

別の注意として、さまざまな整数型に対して特定のビット幅を想定している場合は、明示的にstdint.hで定義された型を使用する必要があります。Visual C ++コンパイラは、私が使用した他のx64またはpower-7コンパイラとは異なる整数サイズ(LLP64)の規則を使用するため、* nixからWindowsに(他の誰かの)コードを移植しているときに、最近これに少し気付きました(LP64)。要するに、32 ビットが必要な場合は、 のような型で明示的に言った方がよいでしょうuint32_t


そのような変換がCで発生した場合、これは常に保持されますか? C標準で定義されていますか?– ジュン

はい、常に保持する必要があります。C99 標準からの関連する引用 (リンク付き): 「整数の昇格は、符号を含む値を保持します。」通常の算術型変換を処理する場合: 「...整数の昇格は両方のオペランドで実行されます。次に、昇格されたオペランドに次の規則が適用されます...」

于 2013-10-09T04:12:01.350 に答える
4

short質問に記載されているように、 16 ビットと 32ビットを想定していますint

unsigned short a = 0xFFFF;

これは、またはに初期化aされます。式の型は; 暗黙的に に変換され、値は保持されます。0xFFFF655350xFFFFintunsigned short

signed short b = 0xFFFF;

これはもう少し複雑です。繰り返します0xFFFFが、タイプはintです。--に暗黙的に変換されsigned shortますが、値が変換の範囲外であるため、値をsigned short保持できません。

値を表すことができない場合、整数を符号付き整数型に変換すると、実装定義の値が生成されます。原則として、 の値はから までbの間の任意の値になり-32768ます+32767。実際には、ほぼ確実に-1. これ以降は、値が であると仮定します-1

unsigned int u16tou32 = a;

の値a0xFFFF、 から に変換さunsigned shortunsigned intます。変換は値を保持します。

unsigned int s16tou32 = b;

の値はbです-1。に変換されますがunsigned int、これは明らかに の値を格納できません-1。整数から符号なし整数型への変換 (符号付き型への変換とは異なります) は、言語によって定義されます。結果は moduloMAX + 1で縮小されます。ここMAXで、 は unsigned 型の最大値です。この場合、 に格納される値s16tou32UINT_MAX - 1、または0xFFFFFFFFです。

signed int u16tos32 = a;

a、 、の値0xFFFFは に変換されsigned intます。値は保持されます。

signed int s16tos32 = b;

b、 、の値-1は に変換されsigned intます。値は保持されます。

したがって、保存された値は次のとおりです。

a == 0xFFFF (65535)
b == -1     (not guaranteed, but very likely)
u16tou32 == 0xFFFF (65535)
s16tou32 == 0xFFFFFFFF (4294967295)
u16tos32 == 0xFFFF (65535)
s16tos32 == -1

整数変換規則を要約すると、次のようになります。

ターゲットの型が値を表すことができる場合、値は保持されます。

それ以外の場合、ターゲットの型が unsigned の場合、値は moduloMAX+1で減らされます。これは、下位 N ビットを除くすべてを破棄することと同じです。これを説明する別の方法はMAX+1、範囲内の結果が得られるまで、値が値に繰り返し加算または値から減算されることです (これは、実際に C 標準で説明されている方法です)。コンパイラは、この繰り返し加算または減算を行うコードを実際には生成しません。彼らはただ正しい結果を得なければなりません。

それ以外の場合、ターゲットの型は署名されており、値を表すことができません。変換により、実装定義の値が生成されます。ほとんどすべての実装で、結果は 2 の補数表現を使用して下位 N ビットを除くすべてを破棄します。(C99 はこの場合のルールを追加し、代わりに実装定義のシグナルを発生させることを許可しました。これを行うコンパイラは知りません。)

于 2013-10-09T04:50:05.400 に答える