C ユーザーズガイド

付録 A ANSI C データ表現

この付録では、ANSI C の記憶装置におけるデータ表現と、関数に引数を渡す仕組みについて説明します。本章は、C 言語以外の言語でモジュールを記述したり使用したい場合に、これらのモジュールに C 言語コードへのインタフェースを持たせるための手引きとして書かれたものです。

記憶装置の割り当て

データ型とその表現方法について表 A-1 にまとめます。

表 A-1 データ型に対する記憶装置の割り当て

データ型 

内部表現 

char 型要素

8 ビット幅のシングルバイト。1 バイトで境界整列される。 

short 型整数

ハーフワード (2 バイト、つまり 16 ビット)。2 バイトで境界 整列される。 

int 型と long

v8 の 32 ビット (4 バイト、つまり 1 ワード)。4 バイトで境界整列される。 v9 では 64 ビット (4 バイト、つまり 1 ワード)。8 バイト境界で整列される。 

long long

(SPARC) 64 ビット (8 バイト、つまり 2 ワード)。 ダブルワードで境界整列される。

(x86) 64 ビット (8 バイト、つまり 2 ワード)。4 バイトで境界整列される。

float

32 ビット (4 バイト、つまり 1 ワード)。4 バイトで境界整列される。1 ビットの符号、8 ビットの指数部および 23 ビットの仮数部から成る。 

double

64 ビット (8 バイト、つまり 2 ワード)。 (SPARC) 8 バイトで境界整列される。 (x86) 4 バイト境界に割り当てられる。 1 ビットの符号、11 ビットの指数部、52 ビットの仮数部から成る。

long double

v8 (SPARC) 128 ビット (16 バイト、つまり 4 ワード)。8 バイトで境界整列される。1 ビットの符号、15 ビットの指数部および 112 ビットの仮数部から成る。 v9 (SPARC) 128 ビット (16 バイト、つまり 4 ワード)。16 バイトで境界整列される。1 ビットの符号、15 ビットの指数部および 112 ビットの仮数部から成る。 (x86) 96 ビット (12 バイト、つまり 3 ワード)。4 バイトで境界整列される。1 ビットの符号、16 ビットの指数部および 64 ビットの仮数部から成る。16 ビットは使用されない。

データ表現

使用しているアーキテクチャによってデータ要素のビット番号の割り当てが異なります。SPARCstation(TM) ではビット 0 を最下位有効ビット、バイト 0 を最上位有効バイトとしてそれぞれ使用します。以下の表に表現方法を示します。

整数表現

ANSI C で使用されている整数型は short、int、long、および long long です。

表 A-2 short の表現 (x86)

ビット 

内容 

 8 - 15

バイト 0 (SPARC)

バイト 1 (x86)

 0 - 7

バイト 1 (SPARC)

バイト 0 (x86)

表 A-3 intlong の表現

ビット 

内容 

 24 - 31

バイト 0 (SPARC) バイト 3 (x86)

 16 - 23

バイト 1 (SPARC)

バイト 2 (x86)

 8 - 15

バイト 2 (SPARC)

バイト 1 (x86)

 0 - 7

バイト 3 (SPARC)

バイト 0 (x86)

表 A-4 long の表現 (Intel、SPARC v8、SPARC v9)

ビット 

内容 

 24 - 31

バイト 0 (SPARC) v8

バイト 4 (SPARC) v9

バイト 3 (x86)

 16 - 23

バイト 1 (SPARC) v8

バイト 5 (SPARC) v9

バイト 2 (x86)

 8 - 15

バイト 2 (SPARC) v8

バイト 6 (SPARC) v9

バイト 1 (x86)

 0 - 7

バイト 3 (SPARC) v8

バイト 7 (SPARC) v9

バイト 0 (x86)

表 A-5 long long の表現

ビット 

内容 

 56 - 63

バイト 0 (SPARC)

バイト 7 (x86)

 48 - 55

バイト 1 (SPARC)

バイト 6 (x86)

 40 - 47

バイト 2 (SPARC)

バイト 5 (x86)

 32 - 39

バイト 3 (SPARC)

バイト 4 (x86)

 24 - 31

バイト 4 (SPARC)

バイト 3 (x86)

 16 - 23

バイト 5 (SPARC)

バイト 2 (x86)

 8 - 15

バイト 6 (SPARC)

バイト 1 (x86)

 0 - 7

バイト 7 (SPARC)

バイト 0 (x86)

浮動小数点表現

floatdoublelong double のデータ要素は、ANSI IEEE 754-1985 規格に従って下の式のように表現されます。

  (-1)s2(e - bias) × j.f

表 A-6 float の表現

ビット 

名称 

31 

符号 (Sign) 

23 - 30 

指数部 (Exponent) 

0 - 22 

仮数部 (Fraction) 

表 A-7 double の表現

ビット 

名称 

63 

符号 (Sign) 

52 - 62 

指数部 (Exponent) 

0 - 51 

仮数部 (Fraction) 

表 A-8 long double の表現 (SPARC)

ビット 

名称 

 127 符号 (Sign)
 112 - 126 指数部 (Exponent)
 0 - 111 仮数部 (Fraction)

表 A-9 long double の表現 (x86)

ビット 

名称 

 81 - 95 使用せず
 80 符号 (Sign)
 64 - 79 指数部 (Exponent)
 63 先行ビット
 0 - 62 仮数部 (Fraction)

詳細については、『数値計算ガイド』を参照してください。

極値表現

正規化された floatdouble の数は「隠された」ビットまたは暗黙のビットを持つと言われます。それにより、精度を 1 ビット分高めることができます。 long double の場合は、先行ビットは暗黙的 (SPARC) または明示的 (x86) のいずれかになります。このビットは正規数に対しては 1、非正規数に対しては 0 になります。

表 A-10 float の表現
 正規数 (0<e<255):

(-1)Sign2(exponent - 127)1.f

非正規数 (e=0, f!=0):(-1)Sign2(-126)0.f
ゼロ (e=0, f=0):(-1)Sign0.0

シグナルを発生する NaN  

s=u, e=255(最大値); f=.0uuu〜uu (少なくとも 1 ビットは 0 以外) 

シグナルを発生しない NaN 

s=u, e=255(最大値); f=.1uuu〜uu 

無限大 

s=u, e=255(最大値); f=.0000〜00 (すべてが 0) 

表 A-11 double の表現
正規数 (0<e<2047):

(-1)Sign2(exponent - 1023)1.f

非正規数 (e=0, f!=0):

(-1)Sign2(-1022)0.f

ゼロ (e=0, f=0):

(-1)Sign0.0

シグナルを発生する 

NaN 

s=u, e=2047(最大値); f=.0uuu〜uu (少なくとも 1 ビットは 0 以外) 

シグナルを発生しない NaN 

s=u, e=2047(最大値); f=.1uuu〜uu 

無限大 

s=u, e=2047(最大値); f=.0000〜00 (すべてが 0) 

表 A-12 long double の表現
 正規数 (0<e<32767):

(-1)Sign2(exponent - 16383)1.f

 非正規数 (e=0, f!=0):

(-1)Sign2(-16382)0.f

 ゼロ (e=0, f=0):

(-1)Sign0.0

シグナルを発生する NaN 

s=u, e=32767(最大値); f=1.0uuu〜uu (少なくとも 1 ビットは 0 以外) 

シグナルを発生しない NaN 

s=u, e=32767(最大値); f=1.1uuu〜uu 

 無限大

s=u, e=32767(最大値); f=1.0000〜00 (すべてが 0)  

重要な数の 16 進数表現

よく使用される数値の16 進数表現を次の表にまとめます。

表 A-13 重要な数の 16 進数表現 (SPARC)
 値floatdoublelong double
 +0 -0

00000000 

80000000 

0000000000000000 

8000000000000000 

 0000000000000000000000000000000080000000000000000000000000000000
 +1.0 -1.0

3F800000 

BF800000 

3FF0000000000000 

BFF0000000000000 

 3FFF00000000000000000000000000000BFFF00000000000000000000000000000
 +2.0 +3.0

40000000 

40400000 

4000000000000000 

4008000000000000 

 4000000000000000000000000000000040080000000000000000000000000000
 +無限 -無限

7F800000 

FF800000 

7FF0000000000000 

FFF0000000000000 

 7FFF00000000000000000000000000000FFFF00000000000000000000000000000
 NaN 7FBFFFFF

7FF7FFFFFFFFFFFF 

 7FFF7FFFFFFFFFFFFFFFFFFFFFFFFFFF

表 A-14 重要な数の 16 進数表現 (x86)
 値floatdoublelong double
 +0-0 0000000080000000 00000000000000000000000080000000 0000000000000000000080000000000000000000
 +1.0-1.0 3F800000BF800000 000000003FF0000000000000BFF00000 3FFF8000000000000000BFFF8000000000000000
 +2.0+3.0 4000000040400000 00000000400000000000000040080000 400080000000000000004000C000000000000000
 +無限-無限 7F800000FF800000 000000007FF0000000000000FFF00000 7FFF8000000000000000FFFF8000000000000000
 NaN 7FBFFFFF FFFFFFFF7FF7FFFF 7FFFBFFFFFFFFFFFFFFF

詳細については、『数値計算ガイド』を参照してください。

ポインタ表現

C 言語におけるポインタは 4 バイトを使用します。NULL 値のポインタはゼロと等価です。

配列の格納

配列は、それぞれの要素が決められた記憶順序で格納されます。各要素は実際には記憶要素の一次元の列に格納されます。

C 言語の配列は行の並びを優先して格納されます。この順序では、多次元配列における右端の添字が最も速く変化します。

文字列データ型は char 要素の配列になります。

表 A-15 自動配列の型と最大の大きさ

型 

最大要素数 

char

268435455 

short

134217727 

int

67108863 

long

67108863 

float

67108863 

double

33554431 

long double

1677215 (SPARC) 22369621 (x86)

long long

33554431 

静的および大域配列にはさらに多くの要素を格納することができます。

極値の算術演算

この節では、浮動小数点の極値と通常値を組み合わせたものに基本算術演算を適用して得られる結果について説明します。

トラップやその他の例外は起こらないものとします。

次の表で、略語の意味を説明します。

表 A-16 略語の使用法

略語 

意味 

 Num

非正規のまたは正規化された数字 

 Inf

無限大 (正または負) 

 NaN

数字ではない 

 Uno

順序不定 

次の表は、異なるタイプのオペランドを組み合わせて行なった算術演算から得られた値のタイプを示しています。

表 A-17 加算と減算の結果

加算および減算 

左のオペランド 

右のオペランド 

Num 

Inf 

NaN 

Num 

Inf 

NaN 

Num 

Num 

注を参照 

Inf 

NaN 

Inf 

Inf 

Inf 

注を参照 

NaN 

NaN 

NaN 

NaN 

NaN 

NaN 


注 -

Num + Num は、結果 が 大きすぎる (オーバーフロー) と Num ではなく Inf になります。Inf + Inf は、無限大の符号が逆であれば NaN になります。


表 A-18 乗算結果

乗算 

左のオペランド 

右のオペランド 

Num 

Inf 

NaN 

NaN 

NaN 

Num 

Num 

Inf 

NaN 

Inf 

NaN 

Inf 

Inf 

NaN 

NaN 

NaN 

NaN 

NaN 

NaN 


注 -


表 A-19 除算結果

除算 

左のオペランド 

右のオペランド 

Num 

Inf 

NaN 

NaN 

NaN 

Num 

Inf 

Num 

NaN 

Inf 

Inf 

Inf 

NaN 

NaN 

NaN 

NaN 

NaN 

NaN 

NaN 


注 -


表 A-20 比較結果

比較 

左のオペランド 

右のオペランド 

+Num 

+Inf 

NaN 

Uno 

+Num 

比較結果 

Uno 

+Inf 

Uno 

NaN 

Uno 

Uno 

Uno 

Uno 


注 -

NaN と比較した NaN は順序不定で、結果は不等価になります。+0 は -0 と比較結果が等しくなります。


引数を渡す仕組み

本節では ANSI C における引数の渡し方法について説明します。

C の関数への引数は、すべて値渡しされます。

実引数は関数の宣言において宣言されるのと逆の順序で渡されます。

実引数が式の場合、関数参照の前に評価されます。その後、式の結果がレジスタに置かれるかスタックにプッシュされます。

(SPARC)

関数は integer 型の結果をレジスタ %o0返します。float 型の結果はレジスタ %f0 に、double 型の結果はレジスタ %f0%f1 に返します。

long long 型 整数は上位ワードは %oN、下位ワードは %o (N+1) というようにレジスタに渡されます。レジスタ内の結果は同様の順序で %i0%i1 に返されます。

double および long double 型を除くすべての引数は 4 バイトの値として渡されます。double 型は 8 バイトの値として渡されます。先頭 6 個の 4 バイト値 (double を 8 と数える) は %o0 から %o5 までのレジスタに渡され、残りはスタック経由で渡されます。構造体の場合は、構造体のコピーが作成され、ポインタがそのコピーに渡されます。long double は構造体と同様に渡されます。

関数から戻った後、スタックから引数をポップするのは呼び出し側の責任です。上記のレジスタは、呼び出し側から見えます。

(x86)

関数は integer 型の結果をレジスタ %eax に返します。

long long の結果はレジスタ %edx%eax に返されます。float、double、 long double 型の結果はレジスタ %st(0) に返されます。

struct、union、long long、double、long double を除くすべての引数は 4 バイト値として渡されます。long long は 8 バイト値として、また long double は 12 バイト値としてそれぞれ渡されます。

structunion はスタックにコピーされます。サイズは 4 の倍数バイトに丸められます。structunion を返す関数は、その structunion を格納する場所を指す隠された最初の引数に渡されます。

関数から戻った後、スタックから引数をポップするのは呼び出し側の責任です (呼び出された関数によってポップされる structunion の余分な引数を除く)。