C言語の解説と演習

(第六回)

メニュー

「N88BASICについて」へ行く

「C言語の解説と演習(第五回)」へ行

トップページへ行く

質問箱へ行く



はじめに

これまでは BASIC で作ったプログラムを C言語のプログラムに書きなおすことを主体に、C言語の勉強をしてきました.今回は、いよいよ、C言語の特徴といえるポインタというものについて解説します.このポインタは、BASIC にはない概念で、ちょっと理解しにくいために、ここで躓く人が非常に多く、C言語を学習する上での鬼門となっています.しかもポインタは C言語のプログラムを書く上で、なくてはならないもので、非常に重要です.今回は気合を入れて、理解できるまで、解説を読み、演習問題をやってみてください.原理は非常に簡単なものですから、いったん理解してしまえば誰でも使えるようになります.
ポインタは非常に重要なので、2回分けて解説を行います。今回は前半です。


その次の大きな一歩(前半)

今回は、まず始めにコンピュータの計算の仕組みについて簡単に説明します.コンピュータはいくつかの部分から構成されていますが、その中で特に重要なものとして CPU とメモリが挙げられます. CPU とは計算を行う部分で、メモリはプログラムや計算結果を記憶する部分です.CPU は例えば MMX Pentium とかPentium II, III とかいうものですね.コンピュータの計算は基本的に CPU がこのメモリからデータを取ってきて計算を行い、その結果をメモリに保存するという操作の繰り返しで行われます.


このメモリには番地(address) というものが割り当てられており、この番地ごとにデータの読みこみや計算結果の保存が行われます.



CPU はデータの読み込みや計算結果の保存をするときは、メモリの番地を指定します.例えば 下図では、102 番の番地のデータを読み込んできて、108 番の番地に計算結果を保存しています.


C言語のプログラムで変数を宣言すると、変数に番地が割り振られます.つまり

	int  a, b, c;
と変数 a, b, c を宣言したとすると、a, b, c にそれぞれ番地(例えば変数 a には、101 番地が、変数 b には、102 番地が、変数 c には 103 番地)が割り振られ、次のような状態になります.



ここで、今、プログラムが

      a = 1;
      b = 2;
      c = a+b;
のようになっているとすると、CPU は次のような操作を行います.
まず、 a  =  1; を見て 1 を変数 a の番地 (101 番地)に保存します.

次に b = 2; を見て 2 を変数 b の番地 (102 番地)に保存します.

最後に、c = a + b; を見て、変数 a の番地(101番地)にあるデータと変数 b の番地(102番地)にあるデータを CPU に読み込み、その和を計算します.そして計算結果を変数 c の番地(103番地)に保存します.

この結果、変数 a の値 1 と変数 b の値 2 の和が変数 c の番地 (103 番地)へ保存されました.C言語のプログラムでは、変数が宣言されるたびに上のように番地が割り振られ、変数の値が割り振られた番地に保存されます.
以上で普通の変数が宣言されたときに、どうなるかがわかりました.では配列が宣言されたときには、どうなるのでしょうか?今、例えばプログラムの中に

      int a, b, c, x[3];

という変数 a, b, c と配列 x の宣言があったとします。実はこの時には、変数 a, b, c と配列の各要素 x[0], x[1], x[2] に番地が割り振られます。

変数 a, b, c と x[0] は必ずしも上のように連続した番地に割り振られるとは限りませんが、配列の各要素 x[0], x[1], x[2] は必ず連続した番地に割り振られることが保証されています。つまり、x[0] の番地が k であれば、x[1],x[2] の番地はそれぞれ k+1,k+2 になります。このことはポインタで重要になってきますから、覚えておいてください。
さて、ではいよいよポインタの解説に入りましょう。ポイントとは一言で言うと、番地を保存するための変数です。こう言ってもすぐにはわからないでしょうから、例で示しましょう。プログラムの中に次に様な宣言があったとします。

      int a, b, c, x[3];
      int *p;
一行目は変数 a, b, c と配列 x[3] の宣言ですね。二行めがポインタの宣言で、整数へのポインタ p を宣言しています。このときの番地の割り振りは次のようになります。

図を見ると、ポインタの p も他の変数と全く同じように番地が割り振られています。では、どこが他の変数と違っているのでしょうか?
実は先ほどちょっと説明しましたが、ポインタは番地を保存するための変数です。つまり、ポインタ p には番地を代入することが可能です。例えば、p に変数 a の番地(101 番地)を代入するには、次のようにします。

      p = &a;
は前に関数 scanf のところで一度だけ出ましたが、変数の番地を与えるための演算子です。変数 a の番地は 101 番地ですから、&a は 101 となります。これが変数 p に代入されるわけですから、上の命令を実行した後、メモリは次の様になります。

ここからが重要ですから、よく聞いてください。ポインタ p には、上で述べたように番地が入りますが、*p とすると p に入っている番地(101番地)に入っているものを表します。わかりにくいと思いますので、例で示しましょう。

今、メモリが上のような状態であるとします。今、p の値は 101 ですから、*p の値は101番地に入っている値、つまり 1 になります。よって

      printf("%d",*p);
という命令があると画面に 1 と表示されるはずです。次に、例えば、下のような命令があるとします。

      *p = 8;
この時も *p は 101番地に入っている値を示しますから、この命令の実行すると、101 番地に 8 が代入され、メモリは次のようになります。

同様にして、次の命令を実行すると

      p = &b;
      *p = 13;
今度は p に変数 b の番地 102 が代入され、さらに p に入っている番地(102 番地)に 13が代入されますから、メモリは次のような状態になります。

それではここで、次の命令を実行してみましょう。

      p = &x[0];
      *p = 1000;
この場合も同様に、p に x[0] の番地(104番地)が代入され、さらに p に入っている番地(104 番地)に 1000 が代入されますから、メモリは次のような状態になりますね.

さて、今度は次のような命令を実行してみましょう。

      *(p+1) = 1001;
今、p には x[0] の番地 104 が入っていますから、p+1 は 105 ですね。よって上の命令は 105番地に 1001 を代入しろという命令になります。つまり命令の実行後、メモリは次の状態になります。

さらに次の命令を実行すると

      *(p+2) = 1002;
メモリが次の状態になるのは、すぐ分かりますね。

以上から、次の命令

      p = &(x[0]);
を実行した後は

の対応があることに気づいたと思います。これは決して偶然ではありません。前に述べたように、配列の要素 x[0], x[1], x[2] は必ず連続した番地に割り振られるので、常に上のような対応があります。つまり、ポインタは配列の代わりに使うことができるということです。
さらにポインタは値を増やしたり、減らしたりすることができます。これも具体例で示しましょう。次のような命令があったとします。

      p = p+1;
      *p = 2001;
これが実行されると、まず p = p+1; で p の値が1つ増やされます。今、p の値は 104 でしたから、この命令が実行された後は p の値は 105 になります。次に *p = 2001; では p の値の番地(105番地)に 2001 を代入するということですから、この命令の実行後はメモリの状態は次のようになります。

つまり、

      *(p+1) = 2001;
の代わりに

      p = p+1;
      *p = 2001;
としてもよいわけです。
また、この後に次の命令

      p = p+1;
      *p = 3001;
を実行すると、メモリの状態が

となることはわかりますね。
あまり一度に多く詰め込みすぎると大変なので、そろそろ今回の解説を終わります.次回はこのポインタを実際に使ったプログラムを示しますので、それまでに今回の解説をよく理解しておいてください。どうしてもわからないことがある人は、質問のページへ書き込みをしてください。今回のまとめを行います。
  1. プログラムは CPU とメモリの間でデータのやり取りしながら、実行される。
  2. メモリには番地があり、CPU は番地を指定して、メモリとのデータのやり取りを行う。
  3. C言語のプログラムで、変数を宣言すると、番地が割り振られる。
  4. C言語のプログラムで、配列を宣言すると、配列の各要素に連続した番地が割り振られる。
  5. ポインタは番地を代入するための変数であり、例えば int へのポインタ p は次の形で宣言する。
    
            int *p;
    
  6. 次の命令を実行すると、変数 a の番地がポインタ p へ代入される。
    
            p = &a;
    
  7. 次の命令を実行すると、
    
            p = &x[0];
            
    配列 x と ポインタ p の間に次の対応ができる。
    
            x[0]  <---->  *p;
            x[1]  <---->  *(p+1);
            x[2]  <---->  *(p+2);
    
  8. ポインタは通常の変数のように、増やしたり、減らしたりすることができる。

次回の予定

次回はポインタの解説の後半部を行います.


演習問題

第5回目の解説で挙げた配列を使った C言語のプログラムを、ポインタを使ったものに書き換えてみよ。