今更ながら,変数の型について


皆さんがこれまで扱ってきた変数の型は,大雑把に,整数型,文字列(配列),浮動小数点型で
覚えていると思います.

分類 データの大きさ 具体的な値の範囲 備考
文字型 char 1byte=4bit -128 〜 127 ASCII文字に対応(英文字,数字,記号)
整数型 short int 2byte=8bit -32768 〜 32767 短整数
int 2byte=8bit または
4byte=16bit
-32768 〜 32767 または
-2147483648 〜 2147483647
処理系によって大きさが変わる
通常は長整数
long int 4byte=16bit -2147483648 〜 2147483647 長整数
浮動小数点型
(実数型)
float 4byte=16bit 最小の正の数1.175494351e-38
最大の正の数3.402823466e+38
単精度
double 8byte=32bit 最小の正の数2.2250738585072014e-308
最大の正の数1.7976931348623158e+308
倍精度
long double 8byte=32bit 最小の正の数2.2250738585072014e-308
最大の正の数1.7976931348623158e+308
と同等またはそれ以上の精度
拡張精度
処理系によって精度が変わる
符号なし文字型 unsigned char 1byte=4bit 0 〜 255 ASCII文字に対応(英文字,数字,記号)
符号なし整数型
(実数型)
unsigned short int 2byte=8bit 0 〜 65535 符号なし短整数
unsigned int 2byte=8bit または
4byte=16bit
0 〜 65535 または
0 〜 4294967295
処理系によって大きさが変わる
通常は長整数
unsigned long int 4byte=16bit 0 〜 4294967295 符号なし長整数

上記の表を見るといくつが疑問がわいてくると思われます.

int, unsigned int, long doubleはいったいどのような大きさになるのでしょうか?
約束事としては,

short int ≦ int ≦ long int, float ≦ double ≦ long double

となっているので,intはどちらか分からず,long doubleもはっきりしなくなってしまうのです.
Windows系では,仕様で,int = long int, double = long doubleとなっていますが,必ずこの
通りではないシステムがあることも知っておきましょう.

文字型になぜunsignedがあるのはなぜでしょうか?
元々は英文字,数字,記号だけのASCII文字に対応していたので,7bit分で収まっていたため,
先頭の符号はどちらでも問題なかったため,符号付きでも符号なしでもよかったというのがあり
ます.ASCII文字以外を使う場合もあり,英語以外の文字は2バイト文字と呼ばれ2バイトで文字を
表現する必要があるため,厳密にはunsigned charとし,文字でなく,文字列として扱うことが必要
となります.

さらに説明を加えると,文字型と言っても整数型のひとつに過ぎないのです.中身は番号ですので
文字そのものの意味はありません.さらにいえば,char = 200と小さな値の整数値を代入することも
できるのですが,上記の範囲からunsigned char = 200 にしないと範囲を超えてしまいます.
授業で積極的に使うことはありませんが,1バイトで示せる整数値という意味もあるのです.

数値の範囲のほか有効桁数を知っておく必要があります.math.hで用意されているような数学演算の
関数の戻り値などは総じてdoubleで返ってきますので,以下のような桁を考慮しておいて下さい.

float 切り捨て5桁〜四捨五入6桁
double 切り捨て15〜四捨五入16桁

キャストと文字列変換関数

型の異なる変数を用いて演算を行う場合,暗黙の了解による型変換が行われて計算されますが,
演算の順番に依存することに注意しなければなりません.
double型の結果が欲しいのにint型が混ざっていたために,小数点以下が切り捨てられながらも,
結果はdouble型となって得られるような問題が発生します.

従って整数型で宣言された変数をdouble型の演算で用いる場合,(int)などを付すことにより,型を
明示的に変換することができます.これをキャストと呼んでいます.

あるいは,stdlib.hに用意されている以下のような関数を用いて,文字列として得た数値を必要な型に
変換することもあります.文字列は変数ではなく,配列ですので単なるキャストはできません.

atoi 関数: 文字列からint型に変換
atol 関数: 文字列からlong int型に変換
atof 関数: 文字列からdouble型に変換
strtol 関数: 文字列からlong int型に変換(変換エラーを返す機能あり)
strtoul 関数: 文字列からunsigned long int型に変換(変換エラーを返す機能あり)
strtod 関数: 文字列からdouble型に変換(変換エラーを返す機能あり)


コンパイルエラーが出ない大きな問題

型については時折厄介な問題を引き起こします.おなじみの関数scanfを例に取りましょう.

引数には変数がありますが,数値を獲得するときに%記号で型を指定します.

%記号の種類

種類 表記例 説明
%d %d int型, short型変数を10進数で表示
%%4d int型, short型変数を10進数で表示
符号を含んで全体を4桁で表示
%ld %ld long型変数を10進数で表示
%f %f float型変数を10進数で表示
%6.3f float型変数を10進数で表示
全体の桁数は6桁、小数点以下は3桁で表示
%lf %lf double型変数を10進数で表示
%16.12lf double型変数を10進数で表示
全体の桁数は16桁、小数点以下は12桁で表示
%c %c char型変数に記憶されている数値に対応したASCIIコードにより文字(1文字)を表示
%s %s char型配列変数に記憶されている文字列を表示
文字列の終端はNULL文字(数値の零)で示す
配列のどれかにNULL文字がなければならない

型が一致していなければならないのですが,上記のように型の大きさによっては都合よく(あやふやに)
変換されてしまうため,コンパイルエラーが出ないという問題があるのです.

実行時には型が合っていないため,エラーが発生します.
例えばですが,doubleで宣言しておいた変数に対し,%fを使ったとします.コンパイルはそれとなくできて
しまうのですが,float型で獲得したものをdouble型にするため不整合が発生します.

scanf関数はポインタとして数値を獲得するため,型の変換など行われず,直接上記のようなやり取りが
あるために起こる問題と言えます.