Comments
Description
Transcript
Lesson 9 配列とポインタ
1 Lesson 9 配列とポインタ 配列とポインタには密接な関係がある。特に配列を関数へ渡す場合は、ポインタを 使って行わなければならない。今回は、ポインタによる配列渡しを学ぶ。 9-1 配列とアドレス 配列とそのアドレスの関係を考えよう int score[5]={85,69,22,98,70}; という配列を宣言したとき、メモリ上でこの配列は右図のように保 存される。このとき、配列の添え字を省略した記述 score は、配列の先頭のアドレスの値を持つ定数になっている。つまり次 の二つは同じ意味になる。 score /* =1000 */ &score[0] /* =1000 */ ところで、 score+1 ∼メモリ上の配列∼ 変数名 住所(アドレス) !""" 85 score[0] !""# 69 score[1] !""$ 22 score[2] !"!% 98 score[3] !"!& 70 score[4] は何を意味するのだろうか? scoreの値は1000番地であるとして も、(score+1)の値は1001番地にはならない。ここで、ポインタ変 数に1を加えるという行為は「アドレスを1要素分すすめる」という 意味を持つ。つまり (score+1)は図で言うと1004番地を示す。よって、*(score+1)と書いたら、この値は69 である。つまり、*(score+k) と score[k] は同じ意味である。 score[0] score[1] score[2] score[3] score[4] == == == == == *score *(score+1) *(score+2) *(score+3) *(score+4) == == == == == 85 69 22 98 70 となる。コンパイラはscore[index]という記述にぶつかったら、これを*(score+index)と解釈して処理して いる。 9-2 関数への配列渡し 以上をふまえて、関数に配列を渡す方法を考えよう。そのためには、関数に配列の先頭のアドレスを渡せ ばよい。関数側では、配列の先頭アドレスを適当なポインタ変数で受け取ればよいのである。たとえば、あ らかじめ数値が入力された配列を受け取り、その配列の各要素の総和を返す関数 sum を作りたいとしよ う。この関数は int sum( int* array ); というプロトタイプをもつことになる。これ使うときは、 goukei = sum( score ); というように、配列の先頭アドレスを引数として渡してあげればよい。そうすると、関数が呼び出された時 点で array=score; 2 という代入が行れ、arrayはscoreと同じ配列として使える。しかし、ここで問題が生じる。というのも、関 数sum内では、ポインタ型で配列を受け取るが、その配列の要素数を知り得ない。簡単な解決方法として は、配列の要素数を引数として渡してあげることである。つまり int sum( int *array, int n ); とする。または配列に対して、最後尾にある特別な数字(例えば -999 など)があるというルールを要請す る方法もある。ここでは後者の方法で、関数sumを作成する。 sample16.c 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: #include<stdio.h> double sum( int *array ); /*プロトタイプ宣言*/ double sum( int *array ) { int i=0, s=0; while(array[i] != -999 ){ s += array[i]; i++; } return( s ); } main() { double total; int score[6]={85,69,22,98,70,-999}; /*最後に-999を入力しておく*/ total = sum( score ); /* 配列の先頭アドレスを渡す*/ printf("点数の合計は%lfです。\n", total ); return 0; } 18行目で関数sumを呼び出すときに、引数としてカッコ内に配列の名前だけを記入している。これにより、 配列の先頭アドレスを渡している。 3行目にあるように、関数sumの引数は、int型のポインタ変数 array である。関数sum内では、array を配 列のように扱うことができる。実行結果は次の通り。 [nagahiro@Tacoma]: ./a.out 点数の合計は344です。 [nagahiro@Tacoma]: 【Problem】sample16.cを作成し、動作を確認せよ。 9-3 ー練習問題ー 3次元ベクトルの計算プログラム 3次元のベクトル v = (vx , vy , vz ) を配列で表現し、計算するプログラムを作ってみよう。二つのベクトルv1とv2を受け取り、その足し算を計 算してv に代入する関数を考える。この関数のプロトタイプは void add( double* v, double* v1, double* v2 ); となるだろう。返り値の型をvoidとしているのは、この関数は値を返さないからである。 3 sample17.c 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: #include<stdio.h> void add( double* v, double* v1, double* v2); void add( double* v, double* v1, double* v2) { int i; for(i=0; i<3; i++){ v[i]=v1[i]+v2[i]; /*和の計算*/ } } int main( void ){ double v1[3]={1,2,3}, v2[3]={2,3,4}, v[3]; int i; wa(v, v1, v2); printf("v=("); /*配列をベクトルらしく表示*/ for( i=0; i<3; i++){ printf(" %.1lf",v[i] ); } printf(" )\n"); } このプログラムを実行すると、次のようになる。 [nagahiro@Tacoma ]: ./a.out v=( 3.0 5.0 7.0 ) [nagahiro@Tacoma ]: 【提出課題】sample16.cに、任意の長さの配列を引数として受け取り、平均値を計算して返す関数 double average( int * array ); を付け加えよ。 【発展課題】sample17.c に以下の機能を付け加える改良をせよ。 1. 一つのベクトルvを受け取り、それを画面に表示する関数 void disp( double* v ); 2. 2つのベクトルv1とv2を受け取り、差v1−v2を計算してvに格納する関数 void sub( double* v, double* v1, double* v2 ); 3. 2つのベクトルv1とv2を受け取り、その内積 v1・v2 を計算して返す関数 double in_p( double* v1, double* v2 ); 4. 1つのベクトルv1とスカラー c を受け取り、そのスカラー倍 cv1 を計算してvに格納する関数 void sc_p( double* v, double* v1, double c ); 5. 2つのベクトルv1とv2を受け取り、外積 v1×v2 を計算してvに格納する関数 void out_p( double* v, double* v1, double* v2 );