基本型の構造とポインタ

このページの内容が分かるとほとんどポインタは完璧です。がんばってください。

2進数とその桁(ビット、bit)の話

コンピュータは電気で動くので、その動作の基本はONとOFFです。

仮に、ONの状態を1、OFFの状態を0で表すとします。スイッチが2つ横に並んでいるとすると

0 0: 2つともOFFの状態
0 1: 右側のスイッチだけONの状態
1 0: 左側のスイッチだけONの状態
1 1:両方ともONの状態

のように4通りの状態を表すことが出来ます。

例えば、

0 0 の状態を 0、 0 1 の状態を 1、 1 0 の状態を 2、そして、1 1の状態を3と見ると約束すると0から3までの数字を2つのスイッチで表すことが出来ます。

また、例えば

0 0 の状態を 0、 0 1 の状態を 1、 1 0 の状態を -1、そして、1 1の状態を-2と見ると約束すると-2から1までの数字を2つのスイッチで表すことが出来ます。

どちらも4通りの数を表しています。

 

今度はスイッチを8個にするとどうなるでしょう。

0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1
0 0 0 0 0 0 1 0
0 0 0 0 0 0 1 1
  (中略)
1 1 1 1 1 1 1 0
1 1 1 1 1 1 1 1

と256通りの数を表すことが出来ます。

このスイッチのことをビットと呼びます。最初のスイッチの組み合わせでは、スイッチは2つなので2ビットで数を表しているといえます。8個の組み合わせは8ビットで数を表しているといえます。

様々なデータ型とビット数

char型

char型は、8ビットで表される数です。よって(2の8乗=)256通りの数を表現できます。charではマイナスの数も表現するので-128から127の数が扱えます。マイナスの数がいらない場合は、

unsigned char a;

のようにcharの前にunsignedを付けます。ただし、この場合はcharunsigned charという別の型があると考えたほうが正解です。unsigned charは0から255までの数を表現できます。

 

int

int型は、実行するパソコンによって異なりますが大抵32ビットです。皆さんの実習室のパソコンも32ビットです。64ビットのパソコンもあります。

32ビットもあるとかなり沢山の数が扱えて、4294967296通り使えます。intもcharと同じくマイナスの数も扱うので–2147483648 から 2147483647まで表現できます。charと同様intにもunsigned intという型があります。この場合0から4294967295までの数を表現できます。

 

double型

double型は更にビットが増えて64ビットで数を表現します。ただし、小数点がつくのでcharやint型よりも各ビットの使われ方は複雑です。ここでは詳細は省きます。

 

ここで、重要なのは型によってビットが異なるので同じ数を表しているとしても内部での表現方法が違うということです。

 

型とメモリの配置の関係

メモリの様子を直感的に説明したいと思います。 下図のように、メモリは、左側にその場所を表す数字(アドレス)と内容を入れる8ビット入れ物が表の様に並んでいるイメージとして考えることができます。プログラムで使われる変数、数字、文字などは 、このメモリという場所に格納されます。また、プログラム自身もメモリに格納されます。

更に下の図は、プログラムの変数に関する記述とそのプログラムの実行後のメモリの内容の関係を具体例で示したものになっています。8ビットを単位としたマス目に変数や数字は配置されてい きます。左側の数字はマスの位置(アドレス)を表す数字です。ポインタに代入される ものは、この マスのアドレスを表す数字なのです。

上の図では、

char a = 1;

として1が代入されたchar型の変数aは200100番のアドレスに格納されています。またその内容はビットで00000001と表現されています。また、

int c = 256;

として256が代入されたint型の変数cは200101番のアドレスに格納されています。ただし、intは32ビットで表されるので8ビットで構成されるマス目4つが使われています。

また、aは200100番に格納されているので&aは200100になります。同様に&cは200101になります。

 注意:インテルのx86系CPU(要は普通のCPU)は、データがメモリに格納される順番は上の説明と逆になります。上の説明の格納のされ方をビッグエンディアンといいます。インテルx86系のCPUの格納のされ方をリトルエンディアンと言います。
リトルエンディアンでは、下位(=桁の位の低い)のバイトが前に置かれます。よって上の図で、リトルエンディアンの場合はアドレス200101,200102,200103,200104に格納される値はそれぞれ、00000000,00000001,00000000,00000000になります。

型変換(キャスト)

charは8ビットでintは32ビットですからchar型の変数の内容をint型の変数に入れること、例えば、

char a = 10;
int b = a;

は大丈夫そうです。しかし、

int a = 256;
char c = a;

は問題があることは、分かるでしょうか?なぜか分からない人はこのページと図をもう一度よく読みなおしてください。

char a = 10;

のaの内容を

int b;

に入れるときは型変換が起こります。

つまり、char型の変数の内容をint型の変数へ代入するときは、8ビットのchar型として表していたものを32ビットのint型の表現に直してからbに入れます。

具体的には、

10 = 00001010 を 10 = 00000000000000000000000000001010に直します。このことを型変換又はキャストと呼びます。

型変換は、主に型の違う変数とデータの代入で起こりますが、更に、色々なところでも起こります。

以前出てきた例ですが、

//プロジェクト名: variable4(既に作成しているはずです。)
//メイン関数
//実行は必ずここから始まる。

int main(int argc, char* argv[])
{

        int a = 1;    //整数型の変数aを使うことを宣言し、1を代入する。
    double b = 1.1; //実数型の変数bを使うことを宣言し、1.1を代入する。

        double c = a + b; //aの内容がdouble型に型変換されてから計算されている。
       
int    d = a + b;
//a+bの計算はdouble型で行われてから、int型に変換される。

       
printf("c = %f, d = %d", c, d); //%d, %fとc, dの順番に注意

    getc(stdin);//Enterキーが押されるまで待つ

    return 0;//処理の終わり
}

 

このプログラムの

double c = a + b;

のようにa とbで型が違うもの同士の計算ではビットの大きいほうの型に変換されます。つまり、aが32ビットの整数型でbが64ビットの実数型(正しくは倍精度浮動少数点型 double precision floating-point)なのでaの値が実数型に変換されます。

それでは、次に進みましょう。