[時間割:C言語]『はじめてのC言語完全入門』その10
2008/11/09 00:27 posted by kunkichi
ここ2、3日格闘していた、ポインタですが、なーんとなく少し壁は超えれたような気がするので、わかってるところだけでもちょっとまとめてみます。あくまでも、なーんとなくのレベル。
はじめてのC言語 完全入門 (標準プログラマーズライブラリ)
posted with amazlet at 08.08.19
塚越 一雄
技術評論社
売り上げランキング: 420963
技術評論社
売り上げランキング: 420963
ポインタ
- 変数は値を記憶する。ポインタ変数はアドレスを記憶する。
- 宣言する場合は、変数名の前に*をつける。あと、ポインタにも型は必要。
int *pval; - ポインタで指定されるアドレスは先頭アドレス。そこから何バイトのデータなのかを判断するために、「型」が必要。(型がわかればバイト数がわかる。sizeof(int)とか)。つまりポインタは型情報を持つということ。
- ポインタへの代入は当たり前だけどアドレスを代入する。通常はアドレス演算子&を使う。変数に&をつけると、その変数の値ではなく、アドレスを参照する。
int *pval;
int val;
pval = &val; - ポインタがさしている別の変数のアドレスから、値を参照する場合は、*を使って逆参照する。
int val = 10;結果は”10″が出力される。
int *pval;
pval=&val;
printf("%d¥n",*pval);
- 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アドレスは4バイトずつ(僕の環境では)増えて、配列の要素を順に参照している。
0xbffff9f4:5
0xbffff9f8:8 - 上の例では参照するポインタを直接指定していたけど、ループを使ってポインタの値をインクリメントしながら参照してみる。
#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: