• 検索結果がありません。

変換

ドキュメント内 note.dvi (ページ 70-74)

第 6 章 C 言語入門

6.8 変数とは

6.8.4 変換

Cのプログラムで演算を行なう時には,数多くの型の変換が行なわれてから演算が実行される.

6.8.4.1 整数への格上げ

汎整数型(char, short, int, long, このような型をintegral typeと呼ぶ.)に対して, 演算を行なう 時には,整数への格上げ(Integral Promotion)(または汎整数拡張)と呼ばれる操作が行なわれること がある.

それは,以下のように定義されている16¿char,shortは,符号つきも符号なしも,整数が使える式で使っ て良い. この時,元の型の全ての値が intで表現できる時には,その値は intに変換される. intで表現 できない時にはunsigned int に変換される. char またはshort 型の変数が unsigned intにしか変 換できないという状況は, short と intが同じバイト幅である時に起こり, この時, unsigned shortは unsigned intに変換されるという意味である. long,unsigned longについては規定されていない.

6.8.4.2 符号拡張

Cではcharは符号つきか符号無しかを規定していない. この時,最上位ビットが 1であるようなchar をintに変換する時の振舞いは処理系依存である. 例えば,最上位ビットが1 として負の数に変換される

(これを符号拡張と呼ぶ)こともあれば, 0として正の数に変換されることもある.

例えば,charが1バイト,intが4バイトのときに,charがsigned charである時には,符号拡張され, intに変換され計算される. この時,前に述べた2進数の表現がとられ,負の数は2の補数表現がとられて いる時,負のsigned char型の変数は,上位ビットに1が埋められ,signed intと扱われる. 一方,char がunsigned charの場合は,常に0が埋められる.

したがって,0x7Fを越える char型の変数を扱う時には, 必ずunsigned charとし,より広い整数への 変換がある時には,符号なしで受けなくてはいけない17. 例えば, char がsigned char の時, char a = 0x80とすると,(int)aは0xFFFFFF80 となるが, charがunsignedの時には, 0x80のままである.

6.8.4.3 符号拡張と整数への格上げの演算への影響

符号拡張と整数への格上げは,char型の変数同士の演算の場合に大きな影響をおよぼす. CPUの演算レ ジスタ長よりも短いメモリサイズを持つ変数に対する演算を行う場合,何らかの形で演算レジスタ長に合

16[1]の定義によれば,「符号なし型はより広い符号なし型に変換される」とされているので注意すること.

17Cの定義によれば,「標準文字セットのすべての文字は正の値を持つ」となっている.

Id: C3.tex,v 1.17 2001-03-20 15:15:41+09 naito Exp

う値(ビットパターン)に変換を行ってから演算を行う必要がある. 符号拡張・整数への格上げは,演算レ ジスタ長に値を合わせる変換と理解して良い.

Example 6.8.4 標準演算レジスタ長が16ビット,1バイトが8ビットである処理系を考えよう. すなわ

ち,intは2バイト長である. さらに,charはsigned charであり,符号拡張を行う処理系であるとする.

この時,次の演算結果はどうなるだろうか?

char c=0x70, d = 0x80 ;

if (d < c) printf("d < c\n") ; else printf("d >= c\n") ;

通常であれば, 0x70 < 0x80であるので,d < cが成り立つはずである. しかし,この結果はd >= cと なる. これは,charが符号付きであり,比較<の演算で整数への格上げが行われるため,符号拡張を受け, 比較の段階で2つの演算レジスタに格納されている値は, dに対応するものは,0xFF80,cに対応するもの は0x0070であり,0xFF80は負の数と判断されるためである.

この結果を正しく判断させるためには, unsigned char c=0x70, d = 0x80 ; if (d < c) printf("d < c\n") ; else printf("d >= c\n") ;

としなければならない. すなわち,文字型変数を「符号なし」と明示的に指定し,符号拡張の影響を排除 しなければならない.

6.8.4.4 整数への変換

任意の整数が符号つき型に変換される時, その数が新しい型で表現可能ならば, その値は不変になるが, そうでない時の結果は処理系依存である.

6.8.4.5 整数と浮動小数点数

浮動小数点数を汎整数に変換する時には,小数部は無視される. また,結果として得られる整数が目的の 型で表現できない時の振舞いは不定である.

逆に,整数を浮動小数点数に変換する時には,その結果が表現可能な範囲にある時でも,正確に表現がで きない時には,一番近い大きな数か小さな数のどちらかに変換される.

6.8.4.6 浮動小数点数

浮動小数点数がより精度の高い浮動小数点数に変換される時には,その値は不定である.

逆に精度の高いものが低いものに変換される時には,その結果が表現可能な範囲にある時でも,正確に表 現ができない時には, 一番近い大きな数か小さな数のどちらかに変換される.

6.8.4.7 算術変換

算術変換(arithmetic conversion) とは,算術演算が行なわれている時に,被演算数の型を揃え, その

結果も同じ型にするという操作である.

ほとんどの演算において,この変換が行なわれる. その手順は,以下の通りである. (条件に一致した最 初の変換が行なわれる).

Id: C3.tex,v 1.17 2001-03-20 15:15:41+09 naito Exp

1. いずれかの被演算数がlong doubleならば,他もlong doubleにする.

2. いずれかの被演算数がdoubleならば,他もdoubleにする.

3. いずれかの被演算数がfloatならば,他もfloatにする.

4. 上のいずれも一致しない時には,整数への格上げを行なって,以下の変換を行なう.

(a) いずれかの被演算数がunsigned longならば,他もunsigned longにする.

(b) いずれかの被演算数がlong で,他がunsigned intである時には,次を調べる.

i. long がunsigned intの全ての数を表現できれば,unsigned intの被演算数はlong intに する.

ii. そうでない時には,全ての被演算数は unsigned longに変換される.

(c) いずれかの被演算数がlong ならば,他もlongにする.

(d) いずれかの被演算数がunsigned intならば, 他もunsigned intにする.

(e) 上のいずれかも当てはまらない時には,被演算数をintとして計算する.

要するに,「算術演算で異なる型の値を指定すると, 型変換が行われる. 変換は情報が欠落しない限り,実 数, 高精度,符号付きの方向で行われる」ということである18.

Example 6.8.5 例えば,longと unsigned intの和は,int=longの場合と,int<longの場合とで, 結果が異なる.

unsigned int a = 1U ; long b = -1L ;

a > b ; /* long = int の時, 正しくない. long > int の時正しい. */

これを正確に判定するには, unsigned int a = 1U ; long b = -1L ;

(long)a > b ;

とする. (後述のSection 6.8.6参照.) Example 6.8.6 intが2バイトである時,

unsigned int a = 256 ;

(a * a * 1L) == (a * (a * 1L)) ; /* この式は正しくない. */

ということが起こる.

Example 6.8.7 次の例は,符号拡張,算術変換などの例である.

18この文章は[6, p. 53]から引用.

Id: C3.tex,v 1.17 2001-03-20 15:15:41+09 naito Exp

int a,b,c ; char n ; double x ; a = 1 ; b = 2 ;

x = a/b ; /* x の値は 0 である. */

x = 1/b ; /* x の値は 0 である. */

x = 1.0/b ; /* x の値は 0.5 になる. 算術変換. */

a = -7 ; b = 2 ;

c = a/b ; /* この値が -3 となるか -4 となるかは処理系依存. */

n = 1 ;

a = n ; /* sizeof(int) = 4, char が符号付きの時,

* a = 0xFFFFFF01 となるか (符号拡張)

* a = 0x00000001 となるかは処理系依存. */

この値を正しく計算するには,後述する型変換を使う必要がある.

6.8.4.8 K&Rでの算術変換

K&RのC言語,すなわち[1]で定義されている, traditional CとANSI Cとでは,算術変換の方法が全

く異なる. K&Rでは,

まず, char または short型の任意の被演算数が intに変換され, float型はdoubleに変換される.

次に,どちらかの被演算数がdoubleなら他方もdoubleに変換さ れ,それが結果の型となる.

そうでなく,一方の被演算数がlong なら他方もlong に変換され, それが結果の型となる.

そうでなく,一方の被演算数がunsignedなら他方もunsignedに 変換され,それが結果の型となる.

そうでないときには,両方の被演算数がintでなければならず,そ れが結果の型となる.

と書かれている. ANSI Cでは「値を保存」する方向に変換が行われるが, K&R C では「unsignedを保 存」する方向に変換が行われる.

Example 6.8.8 次のコード19は, ANSI CとK&R Cでは異なった結果を出力する20 . if (-1 < (unsigned char)1)

printf("-1 is less than (unsigned char)1: ANSI\n") ; else

printf("-1 is NOT less than (unsigned char)1: K&R\n") ;

比較の段階で, ANSI Cの場合は(unsigned char)1が (int)1に格上げされ, intとして比較される.

K&R Cの場合には, -1が unsigned int に変換されたビットパターンとして比較される21 . unsigned

char が unsigned intの場合には, K&R でもANSI でもともにunsigned int としての比較が行われ るため,-1 < (unsigned int)1は「偽」の値を返す.

19[6]からの引用

20実は, K&Rの規約では,unsigned charは存在しない.unsigned,short,longintにつく限定詞と定義されている. しか し,古いANSI規格ではない処理系の多くでunsigned charが利用できる.

21しかし,正しくはK&R Cにはunsigned charは規定されていない.

Id: C3.tex,v 1.17 2001-03-20 15:15:41+09 naito Exp

ドキュメント内 note.dvi (ページ 70-74)