Oracle® Developer Studio 12.5: C ユーザーズガイド

印刷ビューの終了

更新: 2016 年 7 月
 
 

7.3 拡張: 符号なし保存と値の保持

1990 ISO C 規格の「Rationale」(論理的根拠) セクションに、次のような情報があります。「QUIET CHANGE」(メッセージなしの変更)。符号なし保存演算変換に依存するプログラムは、おそらくはメッセージを発行せずに、異なる動作を行います。この変更は、現在広く行われている慣習に対して委員会が行なったもっとも重大な変更であると考えられます。

このセクションでは、この変更がコーディングにどのように影響するかを説明します。

7.3.1 若干の背景となる歴史

プログラミング言語 C』の最初のエディションでは、unsigned は正確に 1 つの型を指定しており、unsigned charunsigned shortunsigned long はありませんでした。ほとんどの C コンパイラにはそれからまもなく、これらが追加されました。一部のコンパイラは unsigned long を実装せず、残りの 2 つを含んでいました。当然、式の中でこれらの新しい型がほかの型と併用されている場合、実装によって異なる型拡張規則が適用されました。

ほとんどの C コンパイラでは、より単純な規則である「符号なし保持」が使用されています。符号なし型を拡張する必要があるときは符号なし型に拡張され、符号なし型が符号付き型と混在するときは結果は符号なし型です。

ISO C で定義されるもう一方の規則は、「値の保持」と呼ばれ、結果の型はオペランドの型の相対的なサイズによって異なります。unsigned char または unsigned short が拡張されるとき、int がより小さい型の値をすべて表現できる大きさである場合は、結果の型は int です。それ以外の場合、結果の型は unsigned int です。この「値の保持」規則は、ほとんどの式に予期されない演算結果になることは少なくなります。

7.3.2 コンパイルの動作

移行モードまたは ISO モード (-Xt または -Xs) でのみ、ISO C コンパイラは符号なし保持拡張を使用します。-std=anyvalue が指定されるか、準拠モード (–Xc) および ISO モード (–Xa) のほかの 2 つのモードでは、値の保持拡張規則が使用されます。

7.3.3 例: キャストの使用

次のコードでは、unsigned charint より小さいと仮定します。

int f(void)
{
    int i = -2;
    unsigned char uc = 1;

   return (i + uc) < 17;
}

このコードは、-xtransition オプションを使用したときに、コンパイラが次の警告を発行する原因になります。

line 6: warning: semantics of "<" change in ISO C; use explicit cast

加算の結果の型は int (値保持) または unsigned int (符号なし保存) です。しかし、どちらの場合でもビットパターンは同じです。2 の補数を使用するマシンでは、次のようになります。

    i:       111...110 (-2)
+   uc:      000...001 ( 1)
===================
        111...111 (-1 or UINT_MAX)

このビット表現は、int では -1 に、unsigned int では UINT_MAX に対応します。したがって、結果の型が int の場合、符号付きの比較が使用され、「小さい」のテストは真になります。結果の型が unsigned int の場合、符号なしの比較が使用され、「小さい」のテストは偽になります。

キャストの加算を使用すると、2 つの動作のうち、どちらを希望するかを指定できます。

value preserving:
    (i + (int)uc) < 17
unsigned preserving:
    (i + (unsigned int)uc) < 17

異なるコンパイラが同じコードに対して異なる意味を選択したため、この式は曖昧になる可能性があります。キャストの加算を使用することにより、コードが読みやすくなると同時に、警告メッセージも発行されなくなります。

同じ動作が、ビットフィールド値の拡張にも適用されます。ISO C では、int または unsigned int ビットフィールド内のビットの数が int 中のビットの数よりも少ない場合、拡張される型は int です。それ以外の場合、拡張される型は unsigned int です。ほとんどの古い C コンパイラでは、明示的な符号なしビットフィールドの場合、拡張される型は unsigned int です。それ以外の場合は int です。

この場合も、キャストを使用することにより、曖昧になることを防ぐことができます。

7.3.4 例: 同じ結果、警告なし

次のコードでは、unsigned shortunsigned char の両方が int よりも狭いと仮定します。

int f(void)
{
    unsigned short us;
    unsigned char uc;
    return uc < us;
}

この例では、2 つの自動変数は int または unsigned int のどちらかに拡張されます。したがって、比較対象は符号なしになることも、符号付きになることもあります。しかし、どちらを選んでも結果は同じなので、警告は発行されません。

7.3.5 整数定数

式と同様に、ある整数定数の型の規則も変更されました。K&R C では、接尾辞なしの 10 進定数の型が int になるのは、その値が int に収まる場合だけでした。接尾辞なしの 8 進数定数または 16 進数定数の型が int になるのは、その値が unsigned int に収まる場合だけでした。それ以外の場合、整数定数の型は long でした。したがって、値が結果の型に収まらないことがありました。1990 ISO/IEC C 規格では、定数の型は、次のリストのうち、値を格納できる最初の型となります。

  • 接尾辞なし 10 進数: intlongunsigned long

  • 接尾辞なし 8 進数または 16 進数: intunsigned intlongunsigned long

  • 接尾辞 U 付き: unsigned intunsigned long

  • 接尾辞 L 付き: long unsigned long

  • 接尾辞 UL 付き : unsigned long

-xtransition オプションを使用すると、ISO C コンパイラは、関係する定数の型決定規則によって動作が異なる可能性のあるすべての式について警告します。古い整数定数型規則が使用されるのは、移行モードだけです。ISO モードと準拠モードでは新しい規則が使用されます。


注 -  接尾辞なしの 10 進定数の型規則は、1999 ISO C 規格に従って変更されています。整数定数を参照してください。

7.3.6 例: 整数定数

次のコードでは、int が 16 ビットであると仮定します。

int f(void)
{
    int i = 0;

    return i > 0xffff;
}

16 進数定数の型が int (2 の補数を使用するマシンで値 – 1) または unsigned int (値 65535) であるため、比較は -Xs および -Xt モードで真になり、-Xa および -Xc モードあるいは -std フラグが指定された場合に偽となります。

この場合も、キャストを適切に使用することにより、コードが読みやすくなり、警告も発行されなくなります。

-Xt, -Xs modes:
    i > (int)0xffff

-Xa, -Xc modes, or when -std flag is specified:
    i > (unsigned int)0xffff
       or
    i > 0xffffU

接尾辞 U 文字は ISO C の新しい機能であるため、古いコンパイラではおそらくエラーメッセージが生成されます。