I ♥ WordPress

[時間割:C言語]『はじめてのC言語完全入門』その10
2008/11/09 00:27 posted by kunkichi

ここ2、3日格闘していた、ポインタですが、なーんとなく少し壁は超えれたような気がするので、わかってるところだけでもちょっとまとめてみます。あくまでも、なーんとなくのレベル。

はじめてのC言語 完全入門 (標準プログラマーズライブラリ)
塚越 一雄
技術評論社
売り上げランキング: 420963


ポインタ

  • 変数は値を記憶する。ポインタ変数はアドレスを記憶する。
  • 宣言する場合は、変数名の前に*をつける。あと、ポインタにも型は必要。int *pval;
  • ポインタで指定されるアドレスは先頭アドレス。そこから何バイトのデータなのかを判断するために、「型」が必要。(型がわかればバイト数がわかる。sizeof(int)とか)。つまりポインタは型情報を持つということ。
  • ポインタへの代入は当たり前だけどアドレスを代入する。通常はアドレス演算子&を使う。変数に&をつけると、その変数の値ではなく、アドレスを参照する。int *pval;
    int val;
    pval = &val;
  • ポインタがさしている別の変数のアドレスから、値を参照する場合は、*を使って逆参照する。int val = 10;
    int *pval;
    pval=&val;
    printf("%d¥n",*pval);
    結果は”10″が出力される。
  • Cの関数では、実引数の値は仮引数にコピーされるため、関数内でどうこうしたとしても実引数には影響しない。つまり、スコープの話。
  • 関数内で実引数を直接変更したい場合はポインタ引数を使う。
    関数の宣言:void pointer_func( int *x, int *y )関数の呼び出し:pointer_func( &a, &b )関数内で値を参照:int val = *a;
  • アドレスを返すような関数でエラーを返す場合はNULLポインタを使う。

配列とポインタ

  • 配列の名前は、配列の先頭アドレスを意味するので、ポインタに代入できる。int ary[] = { 1, 2, 3 };
    int *pval;
    pval = ary;
  • 配列の先頭アドレスは、配列の最初の要素の先頭アドレスでもある。つまり、ポインタに代入した後、逆参照すれば、最初の要素の値を取得できる。※上の続き
    printf("%d¥n", *pval);
    結果は1となり、ary[0]の値となる。
  • ポインタで演算をすると、そのポインタの型が使用するメモリサイズ分だけ加算される。#include <stdio.h>
     
    int main()
    {
      int *p;
      printf("%p\n", p );
      p += 1;
      printf("%p\n", p );
     
      return 0;
    }
    結果は0x1000
    0×1004
    となる。
  • 上記を配列で使うと、2番目以降の要素の参照ができる。#include <stdio.h>
     
    int main()
    {
      int ary[] = {
        1,
        5,
        8
      };
      int *p;
     
      p = ary;
     
      printf("%p:%d\n", p,   *p     );
      printf("%p:%d\n", p+1, *(p+1) );
      printf("%p:%d\n", p+2, *(p+2) );
     
      return 0;
    }
    結果:0xbffff9f0:1
    0xbffff9f4:5
    0xbffff9f8:8
    アドレスは4バイトずつ(僕の環境では)増えて、配列の要素を順に参照している。
  • 上の例では参照するポインタを直接指定していたけど、ループを使ってポインタの値をインクリメントしながら参照してみる。#include <stdio.h>
     
    int main()
    {
      int ary[] = {
        1,
        5,
        8
      };
      int *p;
     
      p = ary;
     
      int i;
      for( i=0; i<(sizeof ary / sizeof ary[0]); i++ )
        printf( "%d:%p:%d\n", i, p, *p++ );
     
      return 0;
    }
    結果は同じ0:0xbffff9f0:1
    1:0xbffff9f4:5
    2:0xbffff9f8:8
  • ポインタを配列のように使う。上の例のforループの部分を以下のように書き換えてみる。  for( i=0; i<(sizeof ary / sizeof ary[0]); i++ )
        printf( "%d:%p:%d\n", i, &p[i], p[i]);
    結果はこれまた同じ。0:0xbffff9ec:1
    1:0xbffff9f0:5
    2:0xbffff9f4:8
    つまり、配列のようにインデックスで指定して、元の配列要素の値を取得できる。
  • 今度は逆に配列をポインタのように使う。include <stdio.h>
     
    int main()
    {
      int ary[] = {
        1,
        5,
        8
      };
     
      int i;
      for( i=0; i<(sizeof ary / sizeof ary[0]); i++ )
        printf( "%d:%d\n", i, *(ary+i) );
     
      return 0;
    }
    結果はこうなる。0:1
    1:5
    2:8
    配列の各要素に、ポインタで使った逆参照演算子*を使って値を参照することができる。
  • 配列とポインタの違いは、
    • ポインタは変数であり、メモリ上にアドレスを記録するための領域が確保される。従って値を変更することができる。
    • 配列名は、コンパイラがコンパイル中に使用する「定数」なので、メモリ上に配列名を記憶するための領域は確保されない(ただし配列の各要素を記録するための領域は確保される)。従って、値を変更することができない。
  • 関数の引数に配列を渡す場合は配列名だけを渡す。int ary[];
    sample func(ary)
  • 関数側の受け取り方は以下の2パターン。配列の要素から最大値を求めるmaxという関数をそれぞれのパターンで書いてみる。
    • 配列で受け取る。例:
      int sample_function(int a[]){
      ・・・
      }
      サンプルコード:#include <stdio.h>
       
      int max(int a[], int size);
       
      int main()
      {
        int ary[] = {
          1,
          5,
          8
        };
        int val;
        val = max( ary, sizeof ary / sizeof ary[0]);
        printf("%d\n",val);
        return 0;
      }
       
      int max(int a[], int size)
      {
        int max = a[0];
        int i;
        for(i = 1;i<size;i++)
          if(max < a[i])
            max = a[i];
        
        return max;
      }
    • ポインタで受け取る。
      例:int sample_function(int *a){
      ・・・
      }
      サンプルコード:#include <stdio.h>
       
      int max(int *a, int size);
       
      int main()
      {
        int ary[] = {
          1,
          5,
          8
        };
        int val;
        val = max( ary, sizeof ary / sizeof ary[0]);
        printf("%d\n",val);
        return 0;
      }
       
      int max(int *a, int size)
      {
        int max = a[0];
        int i;
        for(i = 1;i<size;i++)
          if(max < a[i])
            max = a[i];
        
        return max;
      }
  • 配列を関数に渡す場合、渡されるのは配列の先頭アドレスだけなので、配列の要素数については別に渡してあげる必要があるので注意。上記の例で、関数の引数に配列だけじゃなくて、配列も渡しているのはそのため。試しに配列の要素数を返す関数を作って試してみる。#include <stdio.h>
     
    int arynum(int a[]);
     
    int main()
    {
      int ary[] = {
        1,
        5,
        8
      };
      int val;
      
      val = arynum( ary );
      printf("%d\n",val);
      printf("%d\n",sizeof ary / sizeof ary[0]);
      return 0;
    }
     
    int arynum(int a[])
    {
      int num;
      num = sizeof a / sizeof a[0];
      return num;
    }
    結果は以下。1
    3

ふぅー、途中ちょっと別の本も読んでみたりしつつでしたが、何度か繰り返し読んで、サンプルコードを自分で書いてみて、そんなこんなしてるうちに何となくわかってきました。やっぱり「何度も読む」ことで見えてこなかったものが少しずつ見えてくるんだよね。とりあえず最大の難関を終えてホッとしました。

次回は「文字列と文字配列」です。

コメント&トラックバック

トラックバックURL





このページの先頭へ