ナビゲーションリンクをスキップ | |
印刷ビューの終了 | |
Oracle Solaris Studio 12.3: C ユーザーガイド Oracle Solaris Studio 12.3 Information Library (日本語) |
1990 ISO C 規格の「Rationale」(論理的根拠) 節に、次のような情報があります。「QUIET CHANGE」(メッセージなしの変更)。符号なし保存演算変換に依存するプログラムは、おそらくはメッセージを発行せずに、異なる動作を行います。この変更は、現在広く行われている慣習に対して委員会が行なったもっとも重大な変更であると考えられます。
この節では、この変更がコーディングにどのように影響するかを説明します。
『プログラミング言語 C』の最初のエディションでは、unsigned は正確に 1 つの型を指定しており、unsigned char、unsigned short、unsigned long はありませんでした。ほとんどの C コンパイラにはそれからまもなく、これらが追加されました。一部のコンパイラは unsigned long を実装せず、残りの 2 つを含んでいました。当然、式の中でこれらの新しい型がほかの型と併用されている場合、実装によって異なる型拡張規則が適用されました。
ほとんどの C コンパイラでは、より単純な規則である「符号なし保持」が使用されています。符号なし型を拡張する必要があるときは符号なし型に拡張され、符号なし型が符号付き型と混在するときは結果は符号なし型です。
ISO C で定義されるもう一方の規則は、「値の保持」と呼ばれ、結果の型はオペランドの型の相対的なサイズによって異なります。unsigned char または unsigned short が拡張されるとき、int がより小さい型の値をすべて表現できる大きさである場合は、結果の型は int です。それ以外の場合、結果の型は unsigned int です。この「値の保持」規則は、ほとんどの式に予期されない演算結果になることは少なくなります。
移行モードまたは ISO モード (-Xt または -Xs) でのみ、ISO C コンパイラは符号なし保持拡張を使用します。ほかの 2 つのモード、準拠 (–Xc) と ISO (–Xa) では、値保持拡張規則が使用されます。
次のコードでは、unsigned char が int より小さいと仮定します。
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 です。
この場合も、キャストを使用することにより、曖昧になることを防ぐことができます。
次のコードでは、unsigned short と unsigned char の両方が int よりも狭いと仮定します。
int f(void) { unsigned short us; unsigned char uc; return uc < us; }
この例では、2 つの自動変数は int または unsigned int のどちらかに拡張されます。したがって、比較対象は符号なしになることも、符号付きになることもあります。しかし、どちらを選んでも結果は同じなので、警告は発行されません。
式と同様に、ある整数定数の型の規則も変更されました。K&R C では、接尾辞なしの 10 進定数の型が int になるのは、その値が int に収まる場合だけでした。接尾辞なしの 8 進数定数または 16 進数定数の型が int になるのは、その値が unsigned int に収まる場合だけでした。それ以外の場合、整数定数の型は long でした。したがって、値が結果の型に収まらないことがありました。1990 ISO/IEC C 規格では、定数の型は、次のリストのうち、値を格納できる最初の型となります。
接尾辞なし 10 進数: int、long、unsigned long
接尾辞なし 8 進数または 16 進数: int、unsigned int、long、unsigned long
接尾辞 U 付き: unsigned int、unsigned long
接尾辞 L 付き: long、 unsigned long
接尾辞 UL 付き : unsigned long
-xtransition オプションを使用するとき、関係する定数の型規則によって式の動作が異なる可能性がある場合は ISO C コンパイラは式について警告します。古い整数定数型規則が使用されるのは、移行モードだけです。ISO モードと準拠モードでは新しい規則が使用されます。
次のコードでは、int が 16 ビットであると仮定します。
int f(void) { int i = 0; return i > 0xffff; }
16 進定数の型は int (2 の補数を使用するマシン上で - 1 の値を持つ) または unsigned int (65535 の値を持つ) のどちらかです。比較結果は、ANSI 以前モード (-Xs) と移行モード (-Xt) では真で、ANSI モード (-Xa) と準拠モード (-Xc) では偽です。
この場合も、キャストを適切に使用することにより、コードが読みやすくなり、警告も発行されなくなります。
-Xt, -Xs modes: i > (int)0xffff -Xa, -Xc modes: i > (unsigned int)0xffff or i > 0xffffU