2 関数の引数にポインターを使う

2.1 変数

2.1.1 参照渡しと値渡しの違い

関数呼出しの時,次のふたつの方法で値--処理すべきデータ--を渡すことができる. これらの例をリスト1とリスト2に示す. いずれの場合もswap()関数により値を交換しようとしている.値渡しであるリスト 1の場合,呼び出された関数で呼出側の変数の値を変化させること ができないので意図した通りに動作していない.

一方,リスト2では,呼び出された関数は呼出元の変数を操作 している.呼び出された側の関数は呼出元の変数のアドレスを知っているから,呼出元の 変数の操作ができる.

   1 #include <stdio.h>
   2 void swap(int a, int b);
   3 
   4 //--------------------------------------------
   5 int main(void){
   6   int hoge=1111, fuga=2222;
   7 
   8   printf("hoge=%d\tfuga=%d\n",hoge,fuga);
   9   swap(hoge, fuga);
  10   printf("hoge=%d\tfuga=%d\n",hoge,fuga);
  11 
  12   return 0;
  13 }
  14 
  15 //-------------------------------------------
  16 void swap(int a, int b){
  17   int temp;
  18 
  19   temp=b;
  20   b=a;
  21   a=temp;
  22 }


\fbox{実行結果}
hoge=1111       fuga=2222
hoge=1111       fuga=2222

   1 #include <stdio.h>
   2 void swap(int *a, int *b);
   3 
   4 //--------------------------------------------
   5 int main(void){
   6   int hoge=1111, fuga=2222;
   7 
   8   printf("hoge=%d\tfuga=%d\n",hoge,fuga);
   9   swap(&hoge, &fuga);
  10   printf("hoge=%d\tfuga=%d\n",hoge,fuga);
  11 
  12   return 0;
  13 }
  14 
  15 //-------------------------------------------
  16 void swap(int *a, int *b){
  17   int temp;
  18 
  19   temp=*b;
  20   *b=*a;
  21   *a=temp;
  22 }


\fbox{実行結果}
hoge=1111       fuga=2222
hoge=2222       fuga=1111

2.1.2 複数の計算結果を呼出元へ知らせる

参照渡しを使うと,グローバル変数を使わないで複数の計算結果を呼出元へ知らせること ができる.リスト3の関数cal()は,和と差,積,商を 一度に計算し,参照渡しを使い4つの値を一度に呼出元へ知らせている.実際には,呼出し元 から指定されたアドレスに,呼び出された関数cal()が計算結果を書き込んでいるの である.
   1 #include <stdio.h>
   2 
   3 void cal(int *wa, int *sa, int *seki, int *sho, int a, int b);
   4 
   5 //----------------------------------------------------------
   6 int main(void){
   7   int add, sub, mul, div;
   8 
   9   cal(&add, &sub, &mul, &div, 33, 3);
  10   printf("add = %d\n", add);
  11   printf("sub = %d\n", sub);
  12   printf("mul = %d\n", mul);
  13   printf("div = %d\n", div);
  14 
  15   return 0;
  16 }
  17 
  18 //----------------------------------------------------------
  19 void cal(int *wa, int *sa, int *seki, int *sho, int a, int b){
  20 
  21   *wa = a+b;
  22   *sa = a-b;
  23   *seki = a*b;
  24   *sho = a/b;
  25 }


\fbox{実行結果}
add = 36
sub = 30
mul = 99
div = 11

2.2 配列

関数の引数に配列名を使う場合,参照渡しとなる.前回の講義で述べたように,配列名は 配列の先頭アドレスを表すポインターである.すなわち,配列名という変数(のようなも の)には,その配列の先頭アドレスが格納されている.その配列名--アドレスの値--を 渡すのであるから,参照渡しである.

2.2.1 一次元配列

リスト4に配列を関数に渡す例を示す.この例を見て分かるように,配列を 渡す場合は次のようにする. -4pt

リスト4の関数reverse()は,配列に格納されている順序を逆に する関数である.動作については,各自考えよ.

   1 #include <stdio.h>
   2 void reverse(int n, int a[]);
   3 
   4 //--------------------------------------------
   5 int main(void){
   6   int hoge[3]={1,2,3};
   7 
   8   printf("hoge=\t%d\t%d\t%d\n",hoge[0],hoge[1],hoge[2]);
   9   reverse(3, hoge);
  10   printf("hoge=\t%d\t%d\t%d\n",hoge[0],hoge[1],hoge[2]);
  11 
  12   return 0;
  13 }
  14 
  15 //-------------------------------------------
  16 void reverse(int n, int a[]){
  17   int i, i_max, temp;
  18 
  19   i_max=n/2;
  20 
  21   for(i=0; i<i_max; i++){
  22     temp=a[i];
  23     a[i]=a[n-1-i];
  24     a[n-1-i]=temp;
  25   }
  26 }


\fbox{実行結果}
hoge=   1       2       3
hoge=   3       2       1

2.2.2 多次元配列

多次元配列になると,仮引数の配列の記述方法に気を付ける必要がある.二次元以上にな ると,一番左側の配列のサイズの記述は不要であるが,左から二番目以降の記述が必要で ある.配列の先頭アドレスから,その添字に対応するデータのアドレスを計算するときに, 配列のサイズのうち最も左側は,その計算に不要である.計算に不要なため,仮引数の配 列の最も左側のサイズは書く必要がない.記述しても,コンパイラーはそれを無視する.

配列の先頭アドレスから添字に対応するデータのアドレスの計算方法は,先週の講義ノートを見よ.

   1 #include <stdio.h>
   2 void reverse(int m, int n, int a[][3]);
   3 
   4 //--------------------------------------------
   5 int main(void){
   6   int hoge[2][3]={{11,12,13},{21,22,23}};
   7 
   8   printf("%d\t%d\t%d\n",hoge[0][0],hoge[0][1],hoge[0][2]);
   9   printf("%d\t%d\t%d\n",hoge[1][0],hoge[1][1],hoge[1][2]);
  10   reverse(2, 3, hoge);
  11   printf("\n");
  12   printf("%d\t%d\t%d\n",hoge[0][0],hoge[0][1],hoge[0][2]);
  13   printf("%d\t%d\t%d\n",hoge[1][0],hoge[1][1],hoge[1][2]);
  14 
  15   return 0;
  16 }
  17 
  18 //-------------------------------------------
  19 void reverse(int m, int n, int a[][3]){
  20   int i, j, j_max, temp;
  21 
  22   j_max=n/2;
  23 
  24   for(i=0; i<m; i++){
  25     for(j=0; j<j_max; j++){
  26       temp = a[i][j];
  27       a[i][j] = a[i][n-1-j];
  28       a[i][n-1-j] = temp;
  29     }
  30   }
  31 }


\fbox{実行結果}
11      12      13
21      22      23

13      12      11
23      22      21

2.3 文字列

文字列定数のアドレスをポインターに格納した場合,そのアドレスを呼び出す関数に渡す ことができる.リスト6に示すように容易に文字列を渡すことができ る.ポインターは便利だ.

もちろん,配列に格納された文字列を渡すこともできる.それは前節の整数が格納された配 列と同じようにすればよい.

   1 #include <stdio.h>
   2 
   3 void my_print(char *str);   // プロトタイプ宣言
   4 //------------------------------------------------
   5 int main(void){
   6   char *s;
   7 
   8   s="Akita National College of Technology";
   9 
  10   my_print(s);
  11 
  12   return 0;
  13 }
  14 
  15 //------------------------------------------------
  16 void my_print(char *str){
  17 
  18   printf("ここは,%s です.\n", str);
  19 
  20 }


\fbox{実行結果}
ここは,Akita National College of Technology です.



ホームページ: Yamamoto's laboratory
著者: 山本昌志
Yamamoto Masashi
平成19年2月23日


no counter