13.メモリー でも取り上げましたが、今回は ポインタ について考察してみます。
一般に 変数 はメモリー上に置かれ、処理系や定義に依存したサイズを持ちます。
でね。
その変数はどこにあるの?
に対する答えを提出するのが
ポインタ
なのです。
ポインタ とはアドレス変数そのものなのです。
データの メモリー上のアドレスを保持する変数の事なのです。
32ビット処理系で 整数型の変数をメモリ上に並べて配置します。
処理系より、整数型の変数は4バイトの領域を必要とします。
つまり、変数は4バイトごとに並ぶ事になります。
上記の条件で、最初の変数のアドレスを起点と考えると、
そのアドレスに 4 を加算すれば 次のデータのアドレスが得られます。
データは4バイトごとに並んでいるのですから、当然と言えば当然ですね。
今、4を加算しましたよね?
その数値を収納した変数そのものを ポインタ と呼ぶわけです。
ポインタはデータのアドレス(番地)を指し示し、それ自体はデータではないのです。
これを 32ビット系C言語で書いてみます。
まず定義です。 何も指定しなければ、コンパイラはソースに書かれたとおりに変数を並べます。
ここで 整数型の変数をブロック(配列)として定義してみます。
int hoge[ ] = { 125, -6948, 6584, 73219785, -323189726955, 0, 978455, -7836 };
※ これは int hoge[8]; と定義して 各要素に数値を代入したのと同義です。
そうすると、コンパイラは これらの変数がメモリ上に4バイトごとに並ぶ形に変換します。
仮に 変数 hoge[0] のアドレスを 0xAAAA0000 とします。
すると次の変数 hoge[1] は 0xAAAA0004 となり、以降 0xAAAA0008、0xAAAA000C、0xAAAA0010 と続きます。
だから最初のアドレスに4を加算すれば 次のデータのアドレスが得られるわけです。
ところが…。
変数型は整数でしたよね?
という事は この処理系ではサイズは最初から4バイトと決まっています。
だったら、「何番目の変数か」を表すには、単純に項目番号だけのほうが判りやすいですね?
という事で、整数型のポインタを定義したのが
int *hoge;
という訳なのです。
元々 ポインタ自体は アドレスを格納するための変数として定義されますから、
変数サイズそのものは 処理系依存で CPUのアドレスバス幅と同じなのです。
ですが、その変数に加算/減算を行う場合、
ポインタのデータ(アドレス値)そのものは変数サイズで行われるのです。
上記の例は整数型でしたが、バイト単位の場合もあります。
それは文字型と呼ばれる変数です。
文字をメモリ上に置く場合、文字列(文章)をそのままい配置します。
但し、そのままでは文字列の終端が判別できませんので、必ず終端記号を付けます。
char *String ="これはテストです。";
をメモリ上に展開すると
これはテストです。0
という形で保持されると言う事です。
このゼロですが、それは数値であり 決して文字としてのゼロではありません。
そして その数値は NULL とも呼ばれます。
処理系によっては 更に変換され '$' が使われる事もあります。(MS-DOS系アセンブラなど)
さらに 先頭文字のアドレスがポインタとして定義され、
ポインタの加減算では1バイトずつアクセスされます。
このように ポインタは処理系依存の変数型によって加減算の処理が異なります。
何より メモリーアクセスをそのまま論理化したものだからです。
アドレスを扱う変数とは言っても、その扱いはアドレスそのものではない事に注意が必要です。
このようにポインタを使う処理系では、ハードウェアに密着した処理が記述可能なので、
実行速度も速く、実行ファイルの大きさも小さくできるのです。
当然、誤記述による暴走も簡単に実行できます。
これが C言語は高級アセンブラ と呼ばれる所以ですね。
まとめ
- ポインタとはメモリー上のデータのアドレスを示すもの
- ポインタの操作は処理系依存
という事ですね。
PS:
C系言語では 処理系の仕様やメモリーイメージなど、具体的なモノを扱います。
このポインタの概念やハードウェアの構造を意識したプログラミングが大切であり必要なのです。
次回は なぜそれが大切なのかを考察してみます。
|