ログイン
kid's world

変数の居場所

データを大量に扱うことが必要になったときは,「ポインタ」や「配列」を使う。今はまだ魅力は感じないかもしれないが,後になればなるほどこれらのありがたみがわかってくるであろう。


はじめに

おさらい

前回は,「変数」について解説した。変数は数値を記憶するために使うことができる。また,変数や定数には「型」があり,覚えられる数値の種類や表現できる値の範囲がさまざまであることを学んだ。そして,わざとその表現の限界を超えるようなプログラムを書いたとき,どのような結果になるのかも試してみた。

変数や型は,プログラミング言語の中核をなすといえるほど重要な要素である。もし理解にあいまいなところがある方は,もう一度読み返していただきたい。

今回のお話

今回は,データを大量に扱うときに必要となる「ポインタ」と「配列」について学んでいく。

これらは非常に密接な関係にあるが,外見はかなり異なる。しかし,これらが本質的には同じことをやっているのだと気づけば,変数,配列,ポインタとメモリの関係がさらにグッと鮮明に見えてくるだろう。

なかでもとりわけ「ポインタ」は理解しにくい概念なのだが,本質を見抜いて自在に使いこなせるようになれば,C言語の1つの山場は越したことになる。ぜひ今回説明する部分をマスターしていただきたい。

変数の居場所

さて,さっそく前回に引き続き,変数とメモリの関係について,さらに深く突っ込んだ話をしよう。

変数は,メモリ上の「どこか」に自分の領域を確保して,その自分専用の領域に整数や実数といった数値を記憶している。それをイメージにすると,Fig. 1のようになる。

Fig. 1: メモリ上の領域を確保した変数

/c/pointer-fig1.png

このハシゴのような図は,メモリを縦に並べて表現したものである。この図の1マスは1バイトを表している。よって,1マスには0と1が8つ記憶できる。1マスに8桁の0と1が並んでいることを想像していただきたい。

メモリは非常に広大な空間である。この図に描かれているのは,広大なメモリ空間のほんの一部分であると解釈していただきたい。図中には,float型変数のfと,int型の変数iが確保した領域(グレーに塗った部分)がある。LSI C-86 Ver. 3.30試食版では,float型変数は4バイト,int型変数は2バイトのメモリを使用する。したがってこの図でも,変数fは4マス,変数iは2マスを確保しているわけだ。

何かの間違いがない限り,濃くなっている塗られた部分がほかの変数によって確保されるということはない。もしそのようなことが起きてしまったら「ほかの変数の値を書き換えると,変数fや変数iの値が知らぬうちに書き換わる」ことになってしまう。そのようなことが起こらないことを保証するのは,コンパイラとOSの役目である。

変数は役目を終えると,確保していた領域を解放する。そうすることで,ほかの変数がその領域を再利用することができるのだ。もし解放しなければ,メモリがいくらあっても足りなくなってしまう。

今までは何げなく変数を宣言して使ってきたが,このようにメモリの確保や解放が裏ではうまく行われているのだ。

メモリの住所

さて,本題はここからである。Fig. 1は,いったいこの広大なメモリ空間の「どの部分」を抜き出してきた図なのだろう?

「この部分です」というためには,その場所を表す表現手段がなければならない。たとえば道路地図を開いてその場所を説明するときは,「住所」で伝えるのが普通である。たとえば「この場所は東京都港区赤坂□□番○○号です」というわけだ。それと同じように,メモリにも住所がある。C言語では住所をアドレスと呼んでいる。べつに英語にしてカッコをつけているわけではないが,そう呼んでいる。

ではメモリのアドレスは具体的にどのように表現するのだろう。住所なら「東京都港区」などと漢字で書くが,メモリのアドレスは漢字ではなく単なる数値である。先頭から1バイトごとに0,1,2,3……と順番に振られている。したがって「この変数が確保したメモリのアドレスは,1452532と1452533です」などと表現する。

ただ,10進数で書くことはまれで,メモリのアドレスは16進数で表現することが多い。「16進数って何?」という方も多いだろう。簡単に説明しよう。

2進数と16進数

コンピュータの世界では,16進数が非常によく使われる。なぜなら,メモリの状態をダイレクトに表現できる2進数との相性がよいからだ。

では,まず私たちがふだん使っている10進数から始めよう。10進数では,その名のとおり値が10進むと位が1上がる。つまり,0から値を増やしていくと,1,2,3,4,5,6,7,8,9と進み,次で位が上がり,1と0を組み合わせて10となる。

2進数は,値が2進むと位が1上がる。つまり,最初は0,次は1と進み,その次に位が上がって10となる。

そして16進数は,値が16進むと位が1上がる。つまり,0から1,2,3,4,5,6,7,8,9と進み,これ以降は数字1文字では表せないのでアルファベットを使い,A,B,C,D,E,Fと進む。そしてこの次に位が上がり,10となる。

参考までに,0から20までの10進数を2進数と16進数で表現した表をTable 1に示す。16進数は2進数との相性がよいといったが,この表を見てそれを感じることはできるだろうか。

実は,2進数の4桁ぶんが16進数の1桁ぶんに相当するのだ。だから,16進数の0〜Fまでが2進数ではどう表現されるのかを覚えてしまえば,簡単に2進数と16進数を変換することができる。たとえば16進数の「A26」を2進数にしたい場合は,Aの「1010」,2の「0010」,6の「0110」をつなぎ合わせて,「101000100110」とする。1バイトは8ビット,つまり8桁の2進数で表すことができる。すなわち1バイトは2桁の16進数で表現することができるわけだ。このあたりに,コンピュータで頻繁に16進数が使われるわけがある。

変数のアドレス

それでは,さらに核心に迫って変数が確保したメモリのアドレスを調べてみよう。今はそれを知ったところで何の利点もなさそうに感じるが,実はこの部分がC言語の中でもっともおもしろいところであり,かつ,初心者がもっともつまずきやすい部分でもある。

変数が確保したアドレスを知るのは実に簡単で,変数の前に「&」をつけるだけである。この「&」はアドレス演算子と呼ばれていて,この演算子の後ろにきた変数の確保したメモリの先頭アドレスを計算してくれるのだ。たとえば変数iが確保したメモリの先頭アドレスを知りたければ,

&i

と書く。

ちなみに,printf関数には,アドレスを表示するための変換文字「%p」があるので,List 1のように書けば「int型の変数iが確保したメモリの先頭アドレス」を表示することができる。

List 1: 変数iのアドレスを表示する

#include <stdio.h>
main()
{
  int i;
  printf("%p\n",&i);
}

さてどのような結果になっただろうか。List 1のプログラムが表示するアドレスは,環境によって異なると思うが,筆者の環境では,「0F38」というアドレスが表示された。つまりこの結果から筆者のPCの中では,変数iのためのメモリがFig. 2のように確保されていたということがわかる。

Fig. 2: 変数iのアドレス

/c/pointer-fig2.png

これで,変数とメモリの関係がより深くわかったことと思う。少しコンピュータの中身が見えてきただろうか。


ポインタ変数」へ進む

広告


©Toshio Koide 1996-2007.

目次

リンクについて

リンクは御自由にどうぞ。

メール

mail.gif

広告