少し詳しい型変換の説明

型変換については、第4章で簡単に説明しましたが、複雑な規則があり、迷うことの多い部分です。ですから、ここで少し詳しく算術型の型変換について説明してみようと思います。

なお、説明中で用いたコードの実行結果は「Borland C++ Compiler 5.5」(int:4バイト short:2バイト char:1バイト)で確認をしています。

 

1.汎整数拡張

文字型や整数型などの汎整数型の場合、型が、int より小さな場合(char, signed char, unsigned char, short, unsigned short)は、演算の最初に int か、表現できなければunsigned int に変換されます。このとき、符号を含めてその値を変えることはありません。

(例)
short a = 30000, b = 30000;
printf("sizeof(a) = %d\n", sizeof(a));
printf("sizeof(b) = %d\n", sizeof(b));
printf("sizeof(~a) = %d\n", sizeof(~a));
printf("sizeof(a + b) = %d\n", sizeof(a + b));	
printf("a + b = %d\n", a + b);
実行結果
sizeof(a) = 2
sizeof(b) = 2
sizeof(~a) = 4       ← int に変換してから~演算
sizeof(a + b) = 4    ← int に変換してから+演算
a + b = 60000        ← 4バイトなので60000が扱える

 

2.型変換

次に、以下の規則にしたがって、暗黙の型変換及び明示的型変換(キャスト)が行われます。

(1) 汎整数型から汎整数型への変換

汎整数型から汎整数型への変換は、符号の有無により、次のように型変換されます。

a)符号付き整数型A ⇒ 符号付き整数型B
Aの型サイズ ≦ Bの型サイズ : 値は不変
Aの型サイズ > Bの型サイズ : 表現可能であれば値は不変 表現不可能であれば処理系依存
b)符号無し整数型A ⇒ 符号無し整数型B
Aの型サイズ ≦ Bの型サイズ : 値は不変
Aの型サイズ > Bの型サイズ : A % (Bの型の表現しうる最大値+1)
(例)
unsigned short a = 1234;
unsigned char b;
b = a;
printf("b = %d\n", b);
printf("a %% (255 + 1) = %d\n", a % 256);
実行結果
b = 210
a % (255 + 1) = 210
c) 符号付き整数型A ⇒ 符号無し整数型B

・A が 0 以上

 Aの型サイズ ≦ Bの型サイズ : 値は不変
 Aの型サイズ > Bの型サイズ : A % (Bの型の表現しうる最大値+1)
(例)
short a = 1234;
unsigned char b;
b = a;
printf("b = %d\n", b);
printf("a %% (255 + 1) = %d\n", a % 256);
実行結果
b = 210
a % (255 + 1) = 210

・Aが負

 Aの型サイズ ≦ Bの型サイズ : A + (Bの型の表現しうる最大値+1)
 Aの型サイズ > Bの型サイズ : (Bの型の表現しうる最大値+1) - (-A値 % (Bの型の表現しうる最大値+1))
(例)
short a = -1234;
unsigned short b;
b = a;
printf("b = %d\n", b);
printf("a + (65535 + 1) = %d\n", a + 65536);
実行結果
b = 64302
a + (65535 + 1) = 64302
d) 符号無し整数型A ⇒ 符号付き整数型B
 Aの型サイズ < Bの型サイズ : 値は不変
 Aの型サイズ ≧ Bの型サイズ : 表現可能であれば値は不変 表現不可能であれば処理系依存

 「LSI C-86 Ver. 3.30c試食版」では、ユーザーズマニュアルの「4.1.1 言語仕様」に「整数の格上げ(Integral promotion)ルールがANSIの定義とは若干異なっています。」と記載されているように、上記の規則に沿わない点がいくつかあります。

(2) 浮動小数点型と汎整数型

a)浮動小数点型 ⇒ 汎整数型
小数部は捨てられます。
整数部の値が汎整数型で表現できない場合は、その動作は未定義となります。
表現できる場合は整数部は不変になります。
b)汎整数型 ⇒ 浮動小数点型
表現できる範囲内にあるが、正確に表現できないならば処理系定義の方法で、最も近い値を選びます。

(3) 浮動小数点型と浮動小数点型

浮動小数点型 ⇒ 浮動小数点型
Aの型サイズ ≦ Bの型サイズ : 値は不変
Aの型サイズ > Bの型サイズ : 表現できる範囲内にあるが、正確に表現できないならば処理系定義の方法で、最も近い値を選びます。

以上をまとめると、

表現可能であれば値は不変、表現不可能であれば上記の規則で変換する。
ということになります。

 

3.通常の算術型変換

2項・3項演算子を用いて算術演算を行う場合には、次の順番で算術変換が行われます。これは、暗黙の型変換及び明示的型変換時に適用されます。

  1. 一方が long double なら、他方を long double に型変換する。
  2. 一方が double なら、他方を double に型変換する。
  3. 一方が float なら、他方を float に型変換する。
  4. 一方が unsigned long int なら、他方を unsigned long int に型変換する。
  5. 一方が long int で、他方が unsigned int で、更に long int が unsigned int のすべてが表現できるならば、long int に型変換する。
    long int が unsigned int のすべてを表現できないならば、unsigned long int に型変換する。
  6. 一方が long int なら、他方を long int に型変換する。
  7. 一方が unsigned int なら、他方を unsigned int に型変換する。
  8. 上記に該当しない場合には、int に型変換する。
上記をまとめると、
 long double > double > float > unsigned long int > long int > unsigned int > int
の順番になります。
int に満たない場合には、「汎整数拡張」が行われます。
なお、「浮動小数点オペランドの値及び浮動小数点式の結果の値は、型が要求する精度や範囲を超えて表現してもよい。ただし、結果の型は、それによって変化しない。」ことになっています。

 

4.明示的型変換(キャスト)

キャストは、「4−2.明示的型変換(キャスト)」で説明したように、キャスト演算子を用いて、強制的に型変換を行うものですが、紛らわしい点がありますので、以下に具体的な使い方をまとめます。

(例1)
以下の演算で、double の演算結果を x に得るようにキャストしたい。
double x;
int a = 10, b = 3;
x = a / b;                              /* x = 3.0 */
この場合、正と正の整数除算で割り切れない場合には、商より小さい最大の整数が結果となるため、「3」になります。
演算結果は x に代入するときに、「2.(2) b)汎整数型 ⇒ 浮動小数点型」の結果、「3.0」になります。
ですから、double の結果を得るには、int の a と b をdouble でキャストすればいいわけです。
x = (double) a / (double) b;            /* x = 3.333333 */
しかし、上記「3.通常の算術型変換 2」より、a か b の一方が double なら他方も double に変換されるので、通常は
x = (double) a / b;                     /* x = 3.333333 */
とします。 このとき、気を回して、
x = (double) (a / b);                   /* x = 3.0 */
とすると、キャスト演算より、() の方が優先順位が高いので、先に int / int が行われ、キャストの意味がありません。
(例2)
以下の演算で、double の演算結果を x に得るようにキャストしたい。
double x;
int a = 10, b = 3, c = 5, d = 2;
x = a / b + c / d;                      /* x = 5.0 */
この場合、右辺は int の演算ですから、小数部は捨てられ「5」になり、x に代入するときに、「5.0」になります。
右辺を double でキャストすればいいわけですが、
x = (double) a / b + c / d;             /* x = 5.333333 */
では、2項+演算子よりも / 演算子の方が優先順位が高いので、(double) a / b の結果とc / d の結果が加算されることになり、 正しい結果を得られません。
この場合には、c / d の方もキャストしなければなりません。
x = (double) a / b + (double) c / d;    /* x = 5.833333 */

 

参考文献

▲▲TOP▲▲


banner
「初心者のためのポイント学習C言語」
Copyright(c) 2000-2004 TOMOJI All Rights Reserved