1.4 ポインタと配列 ポインタ変数は前回説明したように 値の入っているアドレスを示す変数です では 配列はどの ようにメモリ上に格納されるか調べてみましょう ex07.c /* ポインタと配列の関係 */ int a[3]={1, 2, 3; /* int 型の大きさ 3 の配列として宣言 */ int *i; /* int 型へのポインタとして宣言 */ double x[3] = {1.0, 2.0, 3.0; /* double 型の大きさ 3 の配列として宣言 */ double *y; /* double 型へのポインタとして宣言 */ i = &a[0]; /* ポインタ変 i に a[0] の番地を代入 */ y = &x[0]; /* ポインタ変 j に x[0] の番地を代入 */ printf("&a[0] = %p n", &a[0]); /* a[0] の番地を表示 */ printf("&a[1] = %p n", &a[1]); /* a[1] の番地を表示 */ printf("&a[2] = %p n", &a[2]); /* a[2] の番地を表示 */ printf("i = %p n", i); /* ポインタ変数 i の値を表示 */ printf("*i = %d n", *i); /* ポインタ変数 i が示す番地の内容を表示 */ i++; /* i をインクリメントする */ printf("i = %p n", i); /* ポインタ変数 i の値を表示 */ printf("*i = %d n", *i); /* ポインタ変数 i が示す番地の内容を表示 */ printf("&x[0] = %p n", &x[0]); /* x[0] の番地を表示 */ printf("&x[1] = %p n", &x[1]); /* x[1] の番地を表示 */ printf("&x[2] = %p n", &x[2]); /* x[2] の番地を表示 */ printf("y = %p n", y); /* ポインタ変数 y の値を表示 */ printf("*y = %f n", *y); /* ポインタ変数 y が示す番地の内容を表示 */ y++; /* y をインクリメントする */ printf("y = %p n", y); /* ポインタ変数 y の値を表示 */ printf("*y = %f n", *y); /* ポインタ変数 y が示す番地の内容を表示 */ ここで注意すべき点は次のとおりです 配列は連続したアドレスのメモリ上に確保されている ポインタ変数に対し ++ や + などの演算を使うと 変数の大きさを考えて変化する ( もちろん -- - も同様) 8
このことを考えると次のようなことがわかります アパートの例に戻って説明します まず このアパートの 1 号室をさすポインタ x を考えてみましょう ここで x++ とすると次の番地をさすのですから x は 2 号室をさすことになります つまり 1 を加えること が 次の部屋 (= 次のアドレス ) を意味します 4 号室四谷 5 号室五代 6 号室六本木 1 号室一ノ瀬 2 号室二階堂 3 号室三鷹 次に このアパートをさす配列 y を考えてみましょう y[0] が 1 号室 y[1] が 2 号室 y[5] が 6 号室を意味します つまり [ ] の中の数字が 1 増える ことが 次の部屋 (= 次のアドレス ) を意味します こう考えてみると 1 増やす ということがポインタにとっても配列にとっても同じ意味を持っ ていることに気づくはずです 実は C ではポインタも配列も同じものなのです 次の例題をやって みましょう ex08.c /* ポインタと配列 */ int a[] = {36, 84, 65, 55, 92; int *x; printf("&a[0] = %p n", &a[0]); /* a[0] のアドレスを表示 */ printf("a = %p n", a); /* a を表示 */ printf("*a = %d n", *a); /* a の指すアドレスの値の表示 */ printf("a[1] = %d n", a[1]); /* a[1] の値の表示 */ printf("*(a+1) = %d n", *(a + 1)); /* a+1 の指すアドレスの値の表示 */ x = a; printf("x[1] = %d n", x[1]); /* x[1] の値の表示 */ printf("*x = %d n", *x); /* a の指すアドレスの値の表示 */ printf("*(x+1) = %d n", *(x + 1)); /* x+1 の指すアドレスの値の表示 */ ポインタ変数 x に配列 a を代入します これでポインタ変数 x が配列 a をあらわすようになりました 詳しく言えば x の値は a[0] のアドレスということになります すると x[1] のような配列が使えるのです x はポインタ変数のはずでしたが 配列としても使えるわけです 逆に配列であった a をポインタ変数のようにも使えます 9
配列とポインタの違いは 配列で宣言すると 値を入れるためのメモリが確保される ポインタで宣言すると 値を入れるためのメモリが確保されない だけです ほかは大きな違いはありません 値を入れるための場所を確保する必要があるときには 配列を使う必要があることさえ知っていれば よいでしょう 部屋一刻館 [6]; 部屋 という型の大きさ 6 の配列を宣言すれば 6 部屋のアパートが建てられる 住人が住める 一刻館は ここ 部屋 * 一刻館 ; 部屋 という型のポインタ変数を宣言しても一刻館をさす場所が用意されるだけ 住人は住めない あっちってどこだ 一刻館はあるんだろうか? 一刻館はあっち C 言語においては配列は一種のポインタなのです ですから 関数の引数として配列を渡したとき 関数の中で配列の値を変更すると 呼び出しもとでも配列の値が変更されていたのは 引数がポイン タになっていたので当たり前のことなのです 以上のことを知れば 配列でできることはポインタでできることとなります 10
まず 簡単な例をいくつかあげます まずは 整数の和をポインタで実現させる例です ex09.c /* ポインタと配列 ( 整数の和 ) */ int sum_array(int[], int); int sum_pointer1(int *, int); int sum_pointer2(int *, int *); int x[10] = {36, 84, 65, 55, 92, 83, 71, 63, 87, 79; printf(" 合計 ( 配列版 ) = %d n", sum_array(x, 10)); printf(" 合計 ( ポインタ版 1) = %d n", sum_pointer1(x, 10)); printf(" 合計 ( ポインタ版 2) = %d n", sum_pointer2(x, x + 9)); /* 合計を計算する関数 ( 配列版 )*/ int sum_array(int array[], int len) { int i, sum = 0; for(i = 0; i < len; i++) { sum += array[i]; return(sum); /* 合計を計算する関数 ( ポインタ版その 1)*/ int sum_pointer1(int *pointer, int len) { int i, sum = 0; for(i = 0; i < len; i++) { sum += *pointer; pointer++; return(sum); /* 合計を計算する関数 ( ポインタ版その 2)*/ int sum_pointer2(int *x, int *last) { int sum = 0; for( ; x <= last; x++) { sum += *x; return(sum); 11
文字列は終了を表す文字 ' 0' があるので簡単です 文字列の長さを調べる関数をあげます char 型へのポインタを使います ex10.c /* 文字列の長さを調べる関数 */ int length_array(char[]); int length_pointer(char *); char string[81]; printf(" 文字列を入力してください : "); scanf("%80s", string); /* 最大 80 文字まで読み込む */ printf("%s n", string); printf("%d 文字 ( 配列版 ) n", length_array(string)); printf("%d 文字 ( ポインタ版 ) n", length_pointer(string)); return (0); /* 文字列の長さを調べる関数 ( 配列版 ) */ int length_array(char str[]) { int i, len = 0; for(i = 0; str[i]!= ' 0'; i++) { len++; return(len); /* 文字列の長さを調べる関数 ( ポインタ版 ) */ int length_pointer(char *str) { int len = 0; for( ; *str!= ' 0'; str++) { len++; return(len); 12
練習問題 6.3 ポインタを用いて文字列の小文字を大文字に変換する関数 to_upper を作成せよ ただし ポイン タを使い 配列は使わないこと また 変換した個数を戻り値で戻すように作成すること ex11.c /* ポインタを用いて大文字変換 */ int to_upper(char *); char string[81]; int i; printf(" 文字列を入力してください : "); scanf("%80s", string); /* 最大 80 文字まで読み込む */ printf(" 変換前 n%s n", string); i = to_upper(string); printf(" 変換後 n%s n", string); printf("%d 文字変換しました n", i); return (0); int to_upper(char *str) { 練習問題 6.4 2 つのポインタを受け取り 一つ目のポインタから二つ目のポインタの間 ( 両端を含む ) から最大値 を指すポインタを返す関数を完成させなさい それを用いた選択ソート ( 最大値ソート ) のプログラ ムを完成させなさい ex12.c /* 選択ソート ( 最大値ソート ) */ #define LENGTH 10 int *nmax(int *x, int *last); void input_data(int *x, int len); void selection_sort(int *x, int len); void exchange(int *x, int *y); 13
int main(void){ int n[length], i; input_data(n, LENGTH); selection_sort(n, LENGTH); for(i = 0; i < LENGTH; i++) { printf("%3d", n[i]); printf(" n"); /* 選択ソート */ void selection_sort(int *x, int len) { int *i, *imax; for(i = x + len - 1; i > x; i--) { imax = nmax(x, i); if(i!= imax) { exchange(imax, i); int *nmax(int *x, int *last) { /* 値の交換 */ void exchange(int *x, int *y) { int t; /* 値を一時的に保存する変数 */ t = *x; *x = *y; *y = t; /* x と y の指すアドレスに格納された値を交換 */ /* データの入力 */ void input_data(int *x, int len) { int i; for(i = 0; i < len; i++) { printf(" 配列番号 %d: ", i); scanf("%d", x++); 14