KID's World
 

おしながき
トップページ
プログラミング
研究
いんすぴ
色日記
ソフトウェア
KID's Worldの歴史
リンク
小出俊夫について


C言語の要「ポインタ」

コンピュータのメモリ構造と変数の関係を通し、C言語の特徴であるポインタを理解する

 

 ではこれからC言語の中で最もよく使われ、C言語の要である「ポインタ」について説明をします。これを理解すると、今まで何も言わずに隠してきた「文字列をどう扱うか」という問題や、「キーボードから文字を入力するには」という事の理解の助けになります。

 

変数はどこにある?

メモリは土地、アドレスは住所

 メモリとは、右の図のように1byte(0〜255)がながーく縦につながったものと考えられます。そしてその先端から0,1,2…とアドレスがふられています。例えると、メモリは土地、アドレスは住所と考えることができます。そして、今後の説明のために、メモリの内容を「立て札に書かれた文字」とすることにします(ん〜強引だなぁ...)。つまり、下の絵一つが1バイトで、立て札には「0〜255」までの数字が書けます。

変数さんが引っ越してくる

 変数を宣言すると、コンピュータのメモリ空間のどこかにその変数が割り当てられます。どこかは分かりませんし、意識する必要もありません。例えると、宣言とは変数iさんがそこに住むということに似ています。住むからには「住所」が必ずあります。上の絵の場合は、「0番地」です。iさんが初めてそこに来たとき、そこにある立て札には、何が書いてあるか分かりません。昔住んでいた人がそのままにしている場合があるからです。iさんはそこに住むことを決めてから、その土地の立て札を、自由に書き換えることができます。

 このたとえ話を踏まえたうえで、実際の話に戻りましょう。変数が宣言されると、それはメモリのどこかに割り当てられます。そして変数はその「どこか」のアドレスをもつことになります。変数はメモリ上で位置を割り当てられるだけなので、割り当てた場所にどんな数値が入っているか分かりません。0の場合もあるし、そうでない場合もあります。

 例えば次の例を実行してみてください。この例は、宣言したあとに代入をしていないので、どんな値が出てくるか分かりません。(コンパイラによっては「警告: iの値が未定義」などとエラーがでますが、コンパイルはできます)

 
#include <stdio.h>

main()
{
  int i;
  printf("%d\n",i);
}

変数iさんは金持ちだった

 ところで、この例ようにint型変数を宣言すると、メモリ上に変数の使う領域が割り当てられるわけですが、int型は1バイトではありません(昔に説明したこの表を参照してください)。もし1バイトだったら、一つの立て札、つまり0〜255までの数字しか扱えません。しかし二つ以上の立て札を使って、もっと多くの数をあらわそうとします。

 つまり変数さんは、区切られた区画を越えて住むことができるわけです。でもそうすると住所はどうなるのでしょうか。右図の場合、住所は「933」になります。変数のアドレスは、割り当てられたメモリの先頭のアドレスになるわけです。

 何だかめんどくさそうですが、プログラマはそんなことを気にすることなく、使うことができます。もし変数iに1000を代入すると、int型を2バイトで表現するシステムでは、右図のように2バイトを使って1000を表現してくれます。

ところで、どこに住んでるの?

 変数さんは、どこに住んでいるのかを教えてくれます。教えてもらうには、アドレス演算子「&」をつけます。例えば

  printf("%d\n", &i);

と書けば、変数iさんのアドレスが分かります(通常は"%p"を指定しますが、説明の便宜上、"%d"を使用しています)。

 

ポインタ変数

住所を覚えるpさん

 C言語ではその住所を記憶するだけの変数を作ることも可能です。この変数をポインタ変数といいます。この変数は重要なので、しっかり理解してください。

 宣言の方法は、

  int *p;

のように、変数名の前に「*」をつけます。ここで注意したいのは、「int」です。これはポインタ変数がint型なのではありません。int型の変数を指し示すために使うポインタ変数pを宣言したことになります。つまり、int人の住所を覚えるpさんがやってきた、ということです。

 pさんに、さきほどのiさんの住所を教えるには、

  p = &i;

と、します。つまりこの時点で変数pには、変数iのアドレスが入ったことになります。

 pさんは、iさんの住所を知ったわけですから、iさんの家へ行くことができます。立て札にはどんな数字が書いてあるかを知ることができます。pさんに見てきてもらうには、間接演算子「*」をつかいます。ポインタを宣言したときに使った「*」とは違うので注意。

 
#include <stdio.h>

main()
{
  int *p, i=10;

  p = &i;
  printf("%d\n",*p);
}

ちょっとややこしくなってきましたが、これを順に理解すると、以下のようになります。

  1. まず「p = &i;」で、ポインタ変数pに変数iのアドレスが入ります。
  2. printf関数ではpの前に間接演算子「*」がついています。
  3. 「*p」は、そのアドレスに割り当てられたint型変数のように振る舞います。
  4. ...つまりちょうど変数iを指定したのと同じようになります。
  5. 画面に変数iの内容、つまり「10」が表示されます。

pさん、実は犯罪者。

 もう何となく分かったと思いますが、pさんは頭に*をつけられると、iさんのように振る舞います。だから、iさんになりすまして、立て札を書き換えることもできます。

  p = &i;
  *p = 100;

とすると、変数iは「100」になってしまいます。注意。このコードの1行目は絶対はぶいてはいけません。なぜならpさんが引っ越してきたばかりのときは、その土地には詳しくないので、住所を教えてあげなくてはいけないのです。もしpさんが偶然にも永田町の住所(プログラムの置かれたメモリ領域)を覚えていたらどうなるでしょうか。ある日突然国会議事堂の立て札が「100」になっていた...立て札の数字が命の政治機能はその場で停止してしまいます。

 このように、ポインタ変数を使うときは、必ず変数として確保されたアドレスを指すようにする、たとえば

  p = &i;

等として、どこかの変数のアドレスを入れておくことが絶対条件になります。

 

だから、何。

 ちょっとコレだけではポインタがなぜ重要だといわれるのか、良くわかりませんね。しかし文字列を扱う問題、キーボードから文字を入力するという問題に必ずついて回るのが、このポインタなのです。ポインタの特徴は、「アドレスを覚えられる」という、その一点だけです。しかし、アドレスを知っているということはそのメモリの内容をいじることができるということになるのです。これが重要なことです。

 つまり、変数の内容ではなく、「変数自体」を扱いたいという場合に、ポインタは威力を発揮します。少しネタをばらすと、例えばキーボードから文字を入力する場合はscanfという関数を使いますが、この関数には変数ではなく、変数へのポインタを渡します。するとscanfは、そうやって教えてもらったアドレスのメモリへ、キーボードから入力された文字を書きこむのです。

 まぁ、これについては次回、詳しくお教えしましょう。

 

まとめ。

  1. 1byteは8bitsで、0から255までの数字を表すことができる。
  2. 文字も数字としてメモリに格納されている。
  3. メモリには先頭から順に数字がふられていて、それをアドレスという。
  4. 変数はコンパイラによって決められたアドレスのメモリに格納される。
  5. そのアドレスはアドレス演算子「&」によって知ることができる。
  6. 変数を宣言するとき、変数名の前に「*」をつけて宣言するその変数はポインタ変数になる。
  7. ポインタ変数の前に間接演算子「*」をつけると、ポインタ変数が指し示すアドレスを使用する変数のように振る舞う。
  8. ポインタ変数は、必ず変数として確保されたアドレスを指すようにする

(1997/01/31 公開, 1999/03/13 改)

[目次へ] [次へ]

 著作権は全て小出 俊夫にあります。KID's World © 1996-2003 Toshio Koide.

 対応ブラウザについて