汎整数拡張

出典: フリー百科事典『ウィキペディア(Wikipedia)』

出典: フリー百科事典『ウィキペディア(Wikipedia)』
移動先: 案内検索

汎整数拡張(はんせいすうかくちょう、: integral promotion)とは、CC++において整数の扱いをする上で、ある条件のもとにその整数の型を格上げ、あるいは格下げする変換のことをいう。C99では「整数拡張」(integer promotion)、C++ (JIS X3014:2003) では「汎整数昇格」というが、意味は変わらない。

格上げ・格下げ[編集]

格上げとは、より多くの値を表現できる型へ変換することで、要はより多くのビットを持つ型への変換である。格下げとは、現在の型で表現できる最大値を表現できない型へ変換することで、要はより少ないビットを持つ型への変換である。

例として、char型をint型に変換するのは格上げ、int型をchar型に変換するのは格下げである。

格上げ[編集]

格上げをする際、変換後の値は、変換前と変換後の型および値の関係が以下のうちどれかである場合には、変換前の値が維持される。

  • 「符号付き→符号付き」
  • 「符号無し→符号付き」
  • 「符号付き→符号無し」であり、かつ変換前の値が正の数である
  • 「符号無し→符号無し」

ただし、

  • 「符号付き→符号無し」であり、かつ変換前の値が負の数である

という条件の場合に限り、変換後の値は

// 変換前の値をa、変換後の型をT型とする。T_MAXはT型の最大値。
(signed T)a + (1 + T_MAX)

となる。

格下げ[編集]

格下げをする際、変換前の値をa、変換後の型をT型とすると、変換後の値は、変換前と変換後の型および値の関係が以下のうちどちらかである場合には、変換前の値が維持される。

  • 「符号付き→符号付き」であり、かつaがsigned Tで表現可能である
  • 「符号無し→符号付き」であり、かつaがsigned Tで表現可能である

変換前の値が維持されない場合を以下に列挙する。

  • 「符号付き→符号付き」であり、かつaがsigned Tで表現できない場合→処理系依存
  • 「符号無し→符号付き」であり、かつaがsigned Tで表現できない場合→処理系依存
  • 「符号付き→符号無し」であり、かつaが正の数である場合→
a % (1 + T_MAX)
  • 「符号付き→符号無し」であり、かつaが負の数である場合→
(1 + T_MAX) - (-a % (1 + T_MAX))
  • 「符号無し→符号無し」である場合→
a % (1 + T_MAX)

条件と変換結果[編集]

端的に言うと、int型あるいはunsigned int型を使用できる式の中では、char, short, int, intビットフィールドの符号付き・符号無しにかかわらず、それらをint型で元の型の全ての値を表現できるならばint型に、それ以外はunsigned int型に変換するということである。

例えば、unsigned char型とint型の演算では、unsigned char型をint型に格上げする変換が行われる。

#include <limits.h>

int main(void) {
    int n;
    unsigned char uch = UCHAR_MAX;  // UCHAR_MAXはunsigned char型で表現できる最大値

    n = uch + 1;  // (1)
    n = ++uch;    // (2)

    return 0;
}

上記(1)の式ではまず、unsigned char型のオブジェクトであるuchに1を加算する演算を行う。このとき整数1はint型であるから、先程示した汎整数拡張の規則が適用され、unsigned char型のuchは、いったんint型に格上げされる。この処理系ではchar型が8ビットであると仮定すれば、UCHAR_MAXの値は255になる。そしてこの時、uchの中身である255はunsigned char型ではなくint型である。ゆえに、演算結果は256となり、nに代入される値は256である。

次に(2)の式ではまず、unsigned char型のオブジェクトであるuchに前置インクリメント演算子が付いているから、uchに1を加算した値をuchに代入するという計算が行われる。

uch = uch + 1; // ++uchの解釈

これでは、単純代入演算子の右オペランドuch + 1は先程と同じように汎整数拡張が適用されて、uchをint型に変換、そして値は256となる。そして、次にint型の256をunsigned char型のuchに代入する演算が行われる。この時、uchを拡張してint型にし、それに256を容れることはできないので(一時的な型変換ではなくなってしまうからである)、次はint型の256をunsigned char型に格下げする変換が行われることになる。

今回の変換では、上記の「格下げ」項に示した《「符号付き→符号無し」であり、かつaが正の数である場合》の規則である

a % (1 + T_MAX)

が適用される。ここで、aは256、T_MAXはunsigned char型の最大値つまりUCHAR_MAXで255となる。これらの値を上記の公式に代入してみると、結果的にnに代入される値は

256 % (1 + 255)
256 % 256

となり、256を256で割った余りは0になるので、nに代入される値は0になる。

予期せぬ汎整数拡張[編集]

上記「条件と変換結果」項に示したサンプルの式(2)のように、256が入ると思っていた値が0になってしまうことがある。汎整数拡張はコンパイル時に勝手に裏で行われる、「暗黙の型変換」であるため、意外なバグとなる場合が多い。具体的な解決策としては何もないのであるが、汎整数拡張という操作が行われるということを知っているだけで、バグを回避できる場合がある。

また、処理系により変数の型のビット数が異なるので、あるソースをそのまま別の処理系で動作させる際、汎整数拡張により、移行前の処理系では起こり得なかったバグが急に発生するというケースもある。この場合は、変数のビット数に依存しないソースを書くということが何よりの解決策となる。

関連項目[編集]