Oracle® Solaris Studio 12.4: C ユーザーガイド

印刷ビューの終了

更新: 2014 年 12 月
 
 

7.3.3 符号拡張

型の変換と拡張規則はいくぶん曖昧ですから、64 ビットコンパイル環境への移行で、符号拡張はよく問題になります。符号拡張の問題を避けるには、明示的な型変換を使用して、意図した結果を得られるようにしてください。

符号拡張が発生する理由を理解するために、ISO C の変換規則を考えます。32 ビットと 64 ビットのコンパイル環境間での符号拡張に関するほとんどの問題の原因と考えられる変換規則は、次の操作中に効力を生じます。

  • 整数の拡張

    整数を必要とする式では、符号の有無に関係なく、charshortenumerated type、ビットフィールドを使用することができます。

    整数が元の型が取り得る値をすべて保持できる場合、値は整数に変換され、それ以外の場合は、符号なし整数に変換されます。

  • 符号付きと符号なし整数間の変換

    負符号付きの整数を同じまたは大きい型の符号なし整数に拡張する場合は、最初に大きな型符号付き整数に拡張され、次に符号なし値に変換されます。

次のコードを 64 ビットプログラムとしてコンパイルすると、addra.base の両方が符号なしの型であっても、addr 変数は符号拡張されます。

%cat test.c
struct foo {
unsigned int base:19, rehash:13;
};

main(int argc, char *argv[])
{
  struct foo a;
  unsigned long addr;

  a.base = 0x40000;
  addr = a.base << 13;  /* Sign extension here! */
  printf("addr 0x%lx\n", addr);

 addr = (unsigned int)(a.base << 13); /* No sign extension here! */
 printf("addr 0x%lx\n", addr);
}

ここで符号拡張が起きるのは、次のように変換規則が適用されるためです。

  • a.base は、整数拡張規則により符号なし int から int に変換されます。つまり、式の a.base << 13int 型ですが、符号拡張はまだ発生していません。

  • 式の a.base << 13int 型ですが、符号付きと符号なし整数拡張規則により、addr に代入する前に long、次に符号なし long へと変換されます。符号拡張は、int から long に変換したときに発生します。

% cc -o test64 -m64 test.c
% ./test64
addr 0xffffffff80000000
addr 0x80000000
%

同じ例を 32 ビットプログラムとしてコンパイルすると、符号拡張はまったく表示されません。

cc -o test -m32 test.c
%test

addr 0x80000000
addr 0x80000000

変換規則の詳細については、ANSI/ISO C 規格の仕様書を参照してください。この規格には通常の演算変換や整数定数に関する有用な規則も規定されています。