第6講 ポインタの学習
第2話 アドレスと内容
ポインタが魅力的である理由は、
int *a;
または、
int* a;
と宣言したとき、実は同時に2つの変数を用意しているのと同じ状態になるという点です。
(以下の説明は、とりあえずの説明ですが正確な説明は第5話でなされます。)
普通の変数*aとポインタ変数aです。
普通の変数とポインタ変数とはどういうことでしょうか。
下表をご覧ください。
ポインタ変数 | 変数 |
a | *a |
アドレスを収納する変数 | 内容を収納する変数 |
表のアドレスとは、メモリのアドレスです。
メモリのアドレスとは何でしょうか。
変数とは数や文字を入れる箱であると説明してきました。
具体的には、どこに値や文字を入れているのでしょうか。
答えは、メモリです。
メモリとは記憶装置のことです。
変数を宣言する=値や文字を入れる箱を用意するとは、メモリの番地=アドレスを割り当てるということなのです。
番地すなわちメモリの場所を割り当て、割り当てた番地に値や文字を収納しているのです。
int a;
と宣言すると、
アドレス | 変数a |
00000000 |
自動的に番地が割り振られ
a=5;等とするとその番地のメモリ−に
値が入れられるのです。
アドレス | 変数a |
00000000 | 5 |
int a[5];
配列を宣言すると
アドレス | 配列変数 | |
00000000 | a[0] | |
00000000 | a[1] | |
00000008 | a[2] | |
00000012 | a[3] | |
00000016 | a[4] |
5つのアドレスが自動的に割り振られます。
4飛びのアドレスになっている理由は、int型の場合4バイトのメモリ−を必要とするからです。
そして、
a[0]=1;
a[1]=2;
a[2]=3;
a[3]=4;
a[4]=5;
とすると、
アドレス | 配列変数 | |
00000000 | 1 | a[0] |
00000004 | 2 | a[1] |
00000008 | 3 | a[2] |
00000012 | 4 | a[3] |
00000016 | 5 | a[4] |
となるわけです。比喩的に、変数は箱と表現してきましたが、正確には番地が割り当てらたメモリ領域なのです。
このメモリの番地=アドレスを扱うことのできる変数がポインタ変数です。
ポインタ変数は、アドレスを代入したり、アドレスを取り出す(取得する)ことができるのです。
int *a;
と宣言すると、
a(ポインタ変数) | *a(普通の変数) |
アドレスを入れる箱と普通の箱が用意されます。
第1話で
a=(int *)malloc;
としないとエラーするわけは後に説明します、と書きましたが、
理由は上の表にあります。
int a;
のときは、
アドレス | 変数a |
00000000 |
自動的にアドレスが割り振られました。ところが、int *a; のときは
a(ポインタ変数) | *a(普通の変数) |
アドレスが割り振られません。
ですから
*a=5;
等とすると*aの番地が割り振られていませんから、
コンピュータはどこに値5を入れたらよいかわかりません。
a=(int *)malloc;
とすることによって
a(ポインタ変数) | *a(普通の変数) |
00000000 |
と番地が割り振られるのです。
これなら、
*a=5; とすると
a(ポインタ変数) | *a(普通の変数) |
0000000 | 5 |
とできるわけです。int a; と宣言すれば自動的にアドレスが割り振られるのに
int *a; だと何故*aのアドレスが割り振られないの?、とお思いでしょう。
実は、フリーにしておいた方がいろいろと便利だからです。
例えば、コーティングを次のように変更してみましょう。
#pragma endregion
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^
e) {
//変数の宣言
int b;
//ポインタの宣言
int* a;
//ポインタの初期化
a=&b;
//内容代入
*a=5;
label1->Text=(*a).ToString();
}
};
}
実行結果
&はアドレス演算子といい、&bで変数bのアドレスになります。
したがって、a=&b; でaに変数bのアドレスを代入したことになります。
また、
#pragma endregion
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^
e) {
//変数の宣言
int b;
//ポインタの宣言
int* a;
//ポインタの初期化
a=&b;
//内容代入
b=5;
label1->Text=(*a).ToString();
}
};
}
としても、実行結果は
同じです。つまり、a=&b; としたのでbと*aは同じになったのです。
表にすると
a | *a |
&b | b |
となります。つまり、bと*a、&bとaがそれぞれ同じです。
int* a; のとき*aのアドレスをフリーにしておく理由は、ルールに従えば自分で自由に入れることができるからです。
int a; だとコンピュータが勝手にメモリを割り振ってしまうのに対して、ポインタの場合はプログラマがルールの範囲の中で自由に設定できるということです。
(しかし、a=(int *)malloc; によって自動的に割り振らせることもできます。)
プログラミングはすべてメモリで管理されています。
変数がアドレスをもっているだけでなく、後に学習する関数
(といっても苦手意識を持つ必要はありません。関数とは独立部品の意味です。
プラモデルでいえば各パーツです。脳細胞やWebのように各部分が有機的に複雑に絡み合うのではなく、
独立部品をプラモデルの各パーツのように単純に接合して組み立てることを構造化プログラミングというのです。
昔のBASICは、各パーツがまるでスパゲティのように複雑に結びついていました。それで全体を見通すのが大変なプログラムになっていたわけです。
単純に接合する=出口と入り口が1つ=結びつきがただひとつにすることによってプログラミングは比較にならないほどわかりやすくなるのです。)
もアドレスをもっています。
ポインタ変数にアドレスを代入できるということは、
ポインタで変数ばかりか関数まで自在に操れることになります。
関数とは、プログラムを構成する部品ですから、部品をポインタで自在に操れること意味します。
ここにポインタの魅力があります。
さて、コーティングを
#pragma endregion
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^
e) {
//ポインタの宣言
int* a;
//変数の宣言
int i;
//ポインタの初期化(メモリ割り当て)
a=(int *)malloc;
//ポインタに内容を代入
for(i=0;i<5;i++)*(a+i)=2*i+1;
//文字変数の宣言
String^ w=L"";
//ポインタの内容の表示
for(i=0;i<5;i++)w+=(*(a+i)).ToString()+L" ";
label1->Text=w;
}
};
}
に戻してください。
この動きを表にすると
アドレス | 変数の内容 | ||
a=(int *)malloc;による | 00000000 | 1 | *a |
a+1 | 00000004 | 3 | *(a+1) |
a+2 | 00000008 | 5 | *(a+2) |
a+3 | 00000012 | 7 | *(a+3) |
a+4 | 00000016 | 9 | *(a+4) |
尚、アドレスa+1の内容を表示させたいときは、*(a+1)とします。
*a+1では、その値は2になってしまいます。*a+1=1+1=2だからです。
それでは課題を出してこの話を閉じましょう。
Form1の2つLabelを加えて、
for(i=0;i<20;i++)*(a+i)=i+1;
として発生させた1次元データを
int b[4][5],c[5][4],d[10][2];
各配列に代入して
と出力させるソフトに変更してください。
第1話へ 第3話へ