汎整数拡張とは
汎
整数拡張(integral promotion)は、
C言語および
C++において、
整数型の値をより広い範囲を表現できる型に変換する処理です。具体的には、`char`型や`short`型などの
整数型を、演算時に`int`型や`unsigned int`型に自動的に変換することを指します。この変換は、暗黙的に行われるため、プログラマーが意識せずに型の不一致によるエラーを回避するのに役立ちます。
格上げと格下げ
汎
整数拡張は、型の「格上げ」と「格下げ」の2つの側面を持ちます。
- - 格上げ: より多くのビットを持つ、表現範囲の広い型への変換です。例えば、`char`型から`int`型への変換が該当します。
- - 格下げ: より少ないビットを持つ、表現範囲の狭い型への変換です。例えば、`int`型から`char`型への変換が該当します。
格上げの際には、変換後の値は、以下の条件を満たす場合には元の値が維持されます。
- - 符号付き型から符号付き型への変換
- - 符号無し型から符号付き型への変換
- - 符号付き型から符号無し型への変換で、変換前の値が正の数の場合
- - 符号無し型から符号無し型への変換
ただし、符号付き型から符号無し型への変換で、変換前の値が負の数の場合には、変換後の値は別の値に変化します。具体的には、元の値に、変換後の型の表現可能な最大値に1を加えた値を足したものが変換後の値となります。
格下げの際には、変換後の型で表現できる範囲に元の値が収まらない場合、情報が欠落したり、処理系に依存した結果となる可能性があります。
変換のルール
C/
C++では、式の中で`char`, `short`, `int`などの
整数型が混在する場合、これらの型は演算の前に`int`型または`unsigned int`型に変換されます。
- - もし元の型の全ての値を`int`型で表現できる場合は`int`型に変換されます。
- - そうでない場合は`unsigned int`型に変換されます。
この規則により、例えば`unsigned char`型同士の演算でも、実際には`int`型に変換されてから演算が行われます。これにより、演算途中で値がオーバーフローするリスクを減らすことができます。
例
c
unsigned char uc1 = 10;
unsigned char uc2 = 20;
int result = uc1 * uc2; // uc1とuc2はint型に格上げされてから演算される
この例では、`uc1`と`uc2`は`unsigned char`型ですが、掛け算の演算時には`int`型に格上げされます。したがって、演算結果は`int`型になります。
汎整数拡張の注意点
汎
整数拡張は便利な機能ですが、暗黙的に
型変換が行われるため、予期せぬバグの原因となることがあります。
例1
c
int si = -1;
unsigned int ui = 1;
if (si < ui) { // 比較結果は偽になる
// このブロックは実行されない
}
この例では、`si`は`-1`ですが、比較演算の際に`unsigned int`型に変換されます。`-1`を`unsigned int`型に変換すると、非常に大きな正の数になるため、`si < ui`は偽となります。
例2
c
unsigned char uc = 255;
uc++; // ucは0になる
この例では、`uc`を
インクリメントすると、`unsigned char`型では表現できない値になるため、オーバーフローが発生し、`uc`の値は`0`になります。これは、`uc++` の演算の過程で、`uc`が `int` 型に昇格して計算された後、`unsigned char` 型に戻される際に値が切り詰められるためです。
他の言語との比較
C/
C++以外の言語にも、類似の型昇格ルールが存在します。
- - Java: `numeric promotion`というルールがあり、整数型のビット数が厳密に定められています。暗黙的な縮小変換はサポートされません。
- - C#: `type promotion`というルールがあります。符号無し整数型をサポートしますが、暗黙的な縮小変換はサポートしません。また、符号付き整数型と符号無し整数型の比較結果はC/C++と異なり、直感的なものになります。
- - F#: 暗黙的な型昇格を許さず、異なる型同士の演算には明示的な変換が必要です。
まとめ
汎
整数拡張は、C/
C++において
整数型の演算を効率的に行うために重要な機能です。しかし、その動作を理解していないと、予期せぬバグを生む可能性があります。そのため、型の変換ルールを正しく理解し、必要に応じて明示的な
型変換を行うことが重要です。
また、異なる処理系でコードを動作させる場合には、
整数型の
ビット数や表現範囲が異なる可能性があるため、移植性を考慮したコードを書くことが推奨されます。