この付録では、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 int と long の表現
ビット |
内容 |
---|---|
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) |
float、double、long double のデータ要素は、ANSI IEEE 754-1985 規格に従って下の式のように表現されます。
(-1)s2(e - bias) × j.f
s = 符号
e = バイアス付きの指数
j = 先行ビット。e の値によって決まる。long double (x86) では、先行ビットは明示的。その他の場合は暗黙的。
f = 仮数部 (23 ビット)
u = ビットが 0 または 1 を示す。
ビット |
名称 |
---|---|
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) |
詳細については、『数値計算ガイド』を参照してください。
正規化された float と double の数は「隠された」ビットまたは暗黙のビットを持つと言われます。それにより、精度を 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 進数表現を次の表にまとめます。
表 A-13 重要な数の 16 進数表現 (SPARC)値 | float 型 | double 型 | long 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)
値 | float 型 | double 型 | long 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 加算と減算の結果
加算および減算 |
||||
左のオペランド |
右のオペランド |
|||
0 |
Num |
Inf |
NaN |
|
0 |
0 |
Num |
Inf |
NaN |
Num |
Num |
注を参照 |
Inf |
NaN |
Inf |
Inf |
Inf |
注を参照 |
NaN |
NaN |
NaN |
NaN |
NaN |
NaN |
Num + Num は、結果 が 大きすぎる (オーバーフロー) と Num ではなく Inf になります。Inf + Inf は、無限大の符号が逆であれば NaN になります。
乗算 |
||||
---|---|---|---|---|
左のオペランド |
右のオペランド |
|||
0 |
Num |
Inf |
NaN |
|
0 |
0 |
0 |
NaN |
NaN |
Num |
0 |
Num |
Inf |
NaN |
Inf |
NaN |
Inf |
Inf |
NaN |
NaN |
NaN |
NaN |
NaN |
NaN |
除算 |
||||
---|---|---|---|---|
左のオペランド |
右のオペランド |
|||
0 |
Num |
Inf |
NaN |
|
0 |
NaN |
0 |
0 |
NaN |
Num |
Inf |
Num |
0 |
NaN |
Inf |
Inf |
Inf |
NaN |
NaN |
NaN |
NaN |
NaN |
NaN |
NaN |
比較 |
||||
---|---|---|---|---|
左のオペランド |
右のオペランド |
|||
0 |
+Num |
+Inf |
NaN |
|
0 |
= |
< |
< |
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 バイト値としてそれぞれ渡されます。
struct と union はスタックにコピーされます。サイズは 4 の倍数バイトに丸められます。struct と union を返す関数は、その struct や union を格納する場所を指す隠された最初の引数に渡されます。
関数から戻った後、スタックから引数をポップするのは呼び出し側の責任です (呼び出された関数によってポップされる struct や union の余分な引数を除く)。