C言語の解説と演習

(第七回)

メニュー

「N88BASICについて」へ行く

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

トップページへ行く

質問箱へ行く



はじめに

今回は、前回に引き続きポインタについてやります.まだ前回の解説を見ていない人は、まずそれをよく見て理解してください.前回の課題で「第5回目の解説で挙げた配列を使った C言語のプログラムを、ポインタを使ったものに書き換えてみよ」という問題が出ましたが、今回はこの解答の解説を中心に行っていきます.前回も述べましたがポインタは 「C言語を学習する上での鬼門」 であり 「C言語のプログラムを書く上で、なくてはならないもの」ですのでがんばって理解してください.


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

第5回目の解説で挙げた配列を使った C言語のプログラムは次のものでした.

#include <stdio.h>

#define N 5

main()
{

  int d[N];
  int i;

  for (i=1;i<=N;i=i+1) {
    printf("データ=");
    scanf("%d",&d[i-1]);
  }

  for (i=1;i<=N;i=i+1) {
    printf("%d番目のデータ=%d¥n",N-i+1,d[N-i]);
  }
}

「このプログラムをポインタを用いたものに書き換えよ」というのが前回の課題でしたが、解答は以下のとおりになります.

#include <stdio.h>

#define N 5

main()
{

  int d[N];
  int i;
 int *p;
  
  p = &(d[0]);
  for (i=1;i<=N;i=i+1) {
    printf("データ=");
    scanf("%d",p);
    p = p+1;
  }

  p = &(d[N-1]);
  for (i=1;i<=N;i=i+1) {
    printf("%d番目のデータ=%d¥n",N-i+1,*p);
    p = p-1;
  }
}


以下では、第5回目の解説で挙げたCのプログラム(配列を用いたもの)を配列版のプログラム、上のポインタを用いたプログラムをポインタ版のプログラムと呼ぶことにします.
さて、配列版のプログラムとポインタ版のプログラムは見比べてみると、ほとんど同じで違いはほんの数行に過ぎません.違いをあげてみましょう.
まず、配列版のプログラムでは

  int d[N];
  int i;

となっている変数宣言に対して、ポインタ版のプログラムでは int へのポインタ p の宣言が新たに加わり

  int d[N];
  int i;
 int *p;

となっています.次にポインタ版のプログラムでは

  p = &(d[0]);
  for (i=1;i<=N;i=i+1) {


  p = &(d[N-1]);
  for (i=1;i<=N;i=i+1) {

のように for 命令の前にポインタ p への代入を行っています.
そして for 命令の内部は、配列版のプログラムが

    scanf("%d",&d[i-1]);

であるのに対し、ポインタ版のプログラムでは

    scanf("%d",p);
    p = p+1;

となっています.同様に、配列版のプログラムが

    printf("%d番目のデータ=%d¥n",N-i+1,d[N-i]);

であるところは、ポインタ版のプログラムでは

    printf("%d番目のデータ=%d¥n",N-i+1,*p);
    p = p-1;

ですね.
もちろん、配列版のプログラムとポインタ版のプログラムは全く同じ動作をします. ポインタ版のプログラムの動作を追ってみましょう.まず、変数宣言

   int d[N];
    int i;
   int *p;

の部分では、前回説明したようにメモリに変数が次のように割り振られます.




次に

    p = &(d[0]);

でポインタ p に変数 d[0] のアドレス(上の図では 101番地)が代入され、メモリの状態は次のようになります.




次は for 命令

  for (i=1;i<=N;i=i+1) {
    printf("データ=");
    scanf("%d",p);
    p = p+1;
  }

ですので i = 1 から i = N まで i を1つずつ増やしながら

    printf("データ=");
    scanf("%d",p);
    p = p+1;

が繰り返し実行されます.まず i = 1 の時を考えると、このとき p には 101番地つまり 変数 d[0] のアドレスが入ってますから、

    scanf("%d",p);


    scanf("%d",&d[i-1]);

と全く同じになります.
次にポインタ版のプログラムでは

    p = p+1;

としてポインタ p の値を1つ増やしていますので、メモリの状態は




となりますね.つぎに i = 2 となって次の命令が実行されますが、

    scanf("%d",p);

このとき、p には 102番地つまり 変数 d[1] のアドレスが入ってますから、上の命令は

    scanf("%d",&d[i-1]);

と全く同じになります.以下、同様にして i = 3,4,5 の場合も配列版とポインタ版のプログラムが全く同じ動作をすることがわかります.こうして最初の for 命令を終了した後、ポインタ版のプログラムでは

    p = &(d[N-1]);

が実行されます.N は 5 と定義されていますから、p には d[4] のアドレス(上の図では 105番地)が代入され、メモリの状態は次のようになります.



次に for 命令

    for (i=1;i<=N;i=i+1) {
      printf("%d番目のデータ=%d¥n",N-i+1,*p);
      p = p-1;
    }

が実行されますので、i = 1,2,3,4,5 に対して

      printf("%d番目のデータ=%d¥n",N-i+1,*p);
      p = p-1;

が繰り返し、実行されます.まず、i = 1 の時を考えると、このときポインタ p には d[4] のアドレス 105番地が入ってますので、*p は d[4] と同じです(ここがよく分からない人は前回の解説を読みなおしてください).よってポインタ版のプログラムの

      printf("%d番目のデータ=%d¥n",N-i+1,*p);

と配列版のプログラムの

      printf("%d番目のデータ=%d¥n",N-i+1,d[N-i]);

は全く同じ動作をします.
次にポインタ版のプログラムでは

      p = p-1;

でポインタ p の値を1つ減らしますので、メモリの状態は次のようになります.



次に i = 2 となりますが、このときポインタ p には d[3] のアドレス 104番地が入ってますので、*p は d[3] と同じです.よってポインタ版のプログラムの

      printf("%d番目のデータ=%d¥n",N-i+1,*p);

と配列版のプログラムの

      printf("%d番目のデータ=%d¥n",N-i+1,d[N-i]);

は全く同じ動作をします.以下同様にして i = 3,4,5 に対しても、ポインタ版のプログラムの

      printf("%d番目のデータ=%d¥n",N-i+1,*p);

と配列版のプログラムの

      printf("%d番目のデータ=%d¥n",N-i+1,d[N-i]);

は全く同じ動作をすることがわかります.
以上で、配列版のプログラムの解説を終わりますが、理解できましたか?始めは分かりにくいかも知れませんが、これは車の運転と同じで、慣れればすぐできるようになります.慣れるためには、自分でいろいろとやってみることが大切ですので、課題をぜひやってみてください.

最後にちょっと一言.
C言語のプログラムを見ていると、変数 i に対して

      i++

とか

      ++i

とかが出てきますが、これらは

      i = i+1

と同じ意味で、i の値を 1つ増やすという命令です.これは C言語では良く使われますので覚えておいてください.厳密に言うと i++ と ++i ではちょっと違いが有るのですが、これについてはまた後日説明しますので、今日のところはどちらも i の値を1つ増やす命令だと思って下さい.また

      i--

とか

      --i

は i の値を1つ減らす命令で i = i-1 と同じ意味です.
今回のポインタ版のプログラムをこの命令を使って書きなおすと

#include <stdio.h>

#define N 5

main()
{

  int d[N];
  int i;
 int *p;
  
  p = &(d[0]);
  for (i=1;i<=N;++i) {
    printf("データ=");
    scanf("%d",p);
    ++p;
  }

  p = &(d[N-1]);
  for (i=1;i<=N;++i) {
    printf("%d番目のデータ=%d¥n",N-i+1,*p);
    --p;
  }
}


となります.
今回のまとめを行います。
  1. 配列を用いたプログラムをポインタを用いたプログラムに書き換え、その解説を行った.
  2. ++i と i++ は変数 i の値を1つ増やす命令であり、i = i+1 と同じ意味である.
  3. --i と i-- は変数 i の値を1つ減らす命令であり、i = i-1 と同じ意味である.

次回の予定

関数について解説する予定です.


演習問題

「N88BASIC について」「最大値を計算する」のように、配列の中で最も大きな数を求めるプログラムを作れ.なお作ったプログラムをポインタを使ったものに書きなおしてみよ.