番外編
1.C言語のポインタの説明
番外編です。ここではC言語の挫折原因ナンバーワンといわれているポインタについて
ご説明したいと思います。
ここではあくまでポインタというもののイメージを掴んでもらうための説明に留めますので
すでにポインタというもののイメージを掴めている方は読んでも意味がないかもしれません…
では始めます。
まずポインタとは何か!?ですが、ポインタとは変数のアドレスを保持する事の出来る変数、
そう、変数です。
普段使っている int型 や char型 の変数は一度代入された数値は変数が無効になるまで
記憶されつづけます。一体そのデータはどこに記憶されているのかといいますと、当然『メモリ』
に記憶されています。最近ではは標準で128メガバイトとか256メガバイトとか余裕で積むよう
になったあの『メモリ』です。
このメモリ、最近の膨大なメモリ空間を使えるパソコンでのソフト開発では殆ど気にしなく
なりましたが、この256メガバイトや128メガバイトのサイズのメモリには一体 int型
変数や char型 変数が何個記憶できるのでしょうか。
ちょっと脱線しますが考えてみましょう。
まず Windows95以降の環境 で int型の変数一個 のデータを記憶するには4バイトのメモリ
領域が、 char型の変数一個 のデータを記憶するには1バイトのメモリ領域が必要です。
ちなみに1バイトの1024倍が1キロバイト、1キロバイトの1024倍が1メガバイト、
1メガバイトの1024倍が1ギガバイト、1ギガバイトの1024倍が1テラバイトと
なります。
では計算してみましょう。
128メガバイト = 1,024キロバイト × 128 = 131,072キロバイト
131,072キロバイト = 1,024バイト × 131,072 = 134,217,728バイト
134,217,728バイト ÷ 4バイト = 33,554,432個
なんと128MBフルに int型変数 を記憶出来たら 3355万4432個 もの int型変数 を記憶
する事が出来ます。すごいですね。
さてこの膨大なメモリ上のどこかに int型変数1個分のデータを記憶 するわけですが、C言語では
メモリ上のどこに記憶されるかや、メモリにどうやって記憶されているかなどのことは全く気に
せずに、ただ宣言した変数名を書いてそこに数値を代入することで目的は達成できます。
int c ;
c = 100 ;
と言った感じに…
ですが、CPUレベル…つまりCPUが理解できるマシン語レベルでは当然『変数名』なんて
ものはありませんので、別の方法をつかってメモリ上のどこに記憶するか指定しています。
そこでマシン語とほぼ一対一の関係にあるアセンブラコードの命令でメモリに数値を記憶する
コードの例を以下にしめしたいと思います。
MOV [1222], 100
かなりいい加減な記述ですが、『MOV』というのはマシン語の命令で、上記のコードは具体的
には『メモリアドレス「1222」バイト番地に数値100を記憶せよ』という意味になります。
そしてメモリアドレス「1222」というのが256MBや128MBあるメモリ上の「どこ」を示して
います。上記の例では 0 から数えて 1222バイト目 の場所に 100 という数値を記憶する、
というコードになります。メモリが128MBあれば、最大で [134217728]バイト番地 まで
指定できる事になります。
今の説明でもわかったかと思いますがこのようにCPUレベルではメモリの『どこ』を示すの
に1つの数値で表現しています。
話を元に戻します、このようにすべての変数のデータはこの0からメモリの上限値までの
アドレスで指定できるメモリ空間の『どこか』に記憶されていることになります。その『どこ』が
いわゆる変数のアドレスであり、そのアドレスを記憶するための変数がポインタ変数ということになります。
そのポインタ変数を宣言する方法を次に記します。
例として int型変数のアドレスを記憶 するためのポインタ変数を宣言するには
int *Point ;
と、普通の int型変数 を宣言するのと違い変数名の前に『*』を付けます。これで Point は
int型変数のアドレスを記憶できる変数 ということになりました。
では実際に Point に int型変数のアドレス を記憶してみましょう。
とりあえず普通の int型変数 i を宣言します、そして Point に int型変数 i のアドレスを
記憶してみたいと思います。
int *Point ;
int i ;
i = 800 ;
Point = &i ;
となります、『ん?なんだこの「&」は』と思われるかと思います。『&』を付けないと Point
には i の値、つまり 800 が記憶されますが、変数名の前に『&』をつける事によって『変数が記憶
されているメモリ上のアドレス』を表している事となります。
例えば変数 i はメモリアドレス 30 に代入されている値を記憶しているとしたら上記の記述で
Point には 30 という値が記憶される事になります。
さてこれで Point にはめでたく変数 i の値が記憶されているアドレスを記憶する事ができましたが、
一体だからどうした、ということになりますよね。
とりあえず一つの利用方法として記憶したアドレスが示すメモリ領域に値を代入したり参照したりする、
というものがあります。
つまり変数 i でもないのにメモリアドレス 30 に記憶されている数値を参照したり別の値を
代入しちゃったり出来るわけです。これによって当然変数 i の値も変わります。
さてその『記憶したアドレスが示しているメモリ領域にアクセスする方法』ですが、それは宣言した時と
同じようにポインタ変数名の前に『*』をつける事によって『記憶したアドレスが示すメモリ領域』を参照
することが出来ます。
例えば先ほどの例の続きとして
*Point = 100 ;
とすることによって Point が記憶している メモリアドレス30 をデータの格納領域にしている
変数i の内容はめでたく 100 になってしまうわけです。
他にも
int a ;
a = *Point + 300 ;
とすれば int型変数 a には Point が記憶しているメモリアドレス 30 に格納されている値、
今のプログラムの続きだとすると 100 という数値が格納されていますから 100 + 300 = 400
の値が変数 a に記憶される事になります。
おさらい
int *Point ;
int i , a ; // &i : 30 &a: 85
i = 800 ; // i : 800 つまり [30] : 800
Point = &i ; // Point : &i つまり Point : 30
*Point = 100 ; // *Point : 100 つまり [30] : 100
a = *Point * 20 ; // a : *Point * 20 つまり a : [30] * 20
i = i + 60 ; // i : i + 60 つまり i : 100 + 60
(注… &変数 は 変数 のアドレス [数] は アドレス[数] の参照)
こんな感じです。イメージが出来たでしょうか?
なお上の注釈では 変数i や 変数a のメモリアドレスを 30 や 85 と勝手に決めていますが、
実際はCコンパイラやソフトがどのアドレスに記憶するかを決めるので、プログラマが任意に
変数を記憶するアドレスを指定するような事は出来ませんのであしからず、です。
さて int型変数のアドレスを記憶 する ポインタ変数Point ですが、当然 Point も 変数 なので
記憶しているアドレスの内容はメモリ上のどこかに記憶されています。もし Pointの内容 がメモリ
アドレス 120 に記憶されているとしたらメモリアドレス 120 の内容を書き変える事によって ポインタ
変数Point の内容も改変することが出来てしまうのです。
では、int型変数のアドレスを記憶するポインタ変数Pointのアドレス を記憶する ポインタ変数 とは
一体どうやって宣言したらいいのかといいますと、変数名の前に『*』を2つ付けるだけです。
つまり
int *Point ;
で普通の int型変数のアドレスを記憶できるポインタ変数 を表すのですから、
int **Point ;
とする事によって int型変数のアドレスを記憶出来るポインタ変数のアドレスを記憶できるポインタ変数
ということになります。
使い方は同じです。
int *Point ;
int **Point2 ;
int i , a ;
a = 200 ;
i = 800 ;
Point = &i ;
Point2 = &Point ;
*Point2 = &a ;
*Point = 500 ;
問題です。上のプログラムで最終的に int型変数 i と a にそれぞれなんの値が記憶されるでしょうか?
答えは i が 800 で a が 500 です。もしこれがすんなりわかった方でしたらポインタのイメージは結構
つかめていると思います。
内訳
int *Point ; // ( &Point : 120 )
int **Point2 ; // ( &Point2 : 300 )
int i , a ; // ( &i : 30 &a : 85 )
a = 200 ; // ( a : 200 つまり [85] : 200 )
i = 800 ; // ( i : 800 つまり [30] : 800 )
Point = &i ; // ( Point : 30 つまり [120] : 30 )
Point2 = &Point ; // ( Point2 : 120 つまり [300] : 120 )
*Point2 = &a ; // ( *[300] : 85 つまり [120] : 85 )
*Point = 500 ; // ( *[120] : 500 つまり [85] : 500 )
ということになります。括弧内の右側が全部数字で、まるでマシン語みたいですが、実際内部ではこんな
ことが行われています。
さてポインタの利用方法は色々あるのですが、それを説明すると大変なのでここではあくまでイメージ
作りに役立てる程度にしておきたいと思いますのでこれにて説明を終わりにしたいとおもいます。
戻る