授業/C言語基礎/値渡しと参照渡し のバックアップの現在との差分(No.3)


  • 追加された行はこの色です。
  • 削除された行はこの色です。
#freeze
関数に引数を渡す方法には、''値渡し''と''参照渡し''があります。


*値渡し [#b7c09ce3]

関数に値を引数として渡すと、関数は引数を記憶するための領域をメモリー内の別の場所に確保し、受け取った値をそこに代入します。

つまり、呼び出された関数が受け取った引数(変数)は、関数を呼び出したときに渡した引数(変数または定数)とは別の場所に記憶されます。

これを''値渡し''といいます。

値渡しのとき、呼び出された側で受け取った引数の値を変更しても、呼び出した側が渡した引数の値は変更されません。
これは、別の場所に記憶されている変数の値を変更しているだけだからです。

次のプログラムでは、main関数がinc関数に変数 i を渡し、inc関数が受け取った引数 i の値を 1 増やしていますが、main関数で宣言されている変数 i の値は変わりません(プログラム1)。
#geshi(c){{
/*
 *  受け取った引数の値を1増やして出力する
 */
void inc(int i) {{
  i++;
  printf(">> %d\n", i)
}


int main(void) {
  int i = 0;
  inc(i);  // 変数を渡す
  printf("%d\n", i);
  return 0;
}
}}

このプログラムを実行すると、次のようになります。
#geshi(sh){{
luna% a.out
>> 1
0
}}



*参照渡し [#m293c2f1]

関数に、変数を記憶している領域の''アドレス''(''参照'')を渡し、受け取った関数も同じ領域に格納されている変数にアクセスする引数の渡し方を''参照渡し''といいます

参照渡しのとき、呼び出された側で受け取った引数の値を変更すると、呼び出した側が渡した引数の値も変更されます。
渡した引数と同じアドレスの変数にアクセスし、その値を変えたからです。

C言語の参照渡しは、ポインター型の引数に、変数を記憶している領域のアドレスを渡すことによって行いま
す。

ポインターとアドレスについては、C言語応用で勉強する内容なので、ここでは詳しくは説明しません。


*配列は参照渡し [#k58a2022]

配列を関数の引数にすると、配列の要素数は無視されます。

実は、配列を宣言すると、配列名がポインター型の変数となり、配列を記憶している領域の先頭のアドレスを記憶します。

したがって、配列を関数の引数にすることは、ポインター型の引数に配列を記憶している領域の先頭アドレスを渡していることになり、参照渡しと同じになります。


#geshi(c){{
/*
 *  受け取った配列の先頭要素の値を1増やして出力する
 */
void inc(int a[]) {{
  a[0]++;
  printf(">> %d\n", i[0])
  printf(">> %d\n", a[0])
}


int main(void) {
  int a = { 0 };
  int a[] = { 0 };
  inc(a);  // 配列を渡す
  printf("%d\n", a[0]);
  return 0;
}
}}
#geshi(sh){{
luna% a.out
>> 1
1
}}



*プロトタイプ宣言 [#j7eb1684]


これまで、自分で関数を定義するときはmain関数の前、つまり、関数が呼び出される場所よりも前にしていました。

ところが、関数の数が増えてくると、どの関数がどの関数から呼び出されているのかを全て把握し、どの関数からも呼び出される前に関数を定義することは難しくなります。

そこで、''プロトタイプ宣言''を使って関数の宣言だけを先にしておくと、関数の定義は自由な場所でできるようになります。

#geshi(c){{
  戻り値の型 関数名(引数1の型 引数1の名前, 引数2の型 引数2の名前, ...);
}}

プロトタイプ宣言がないと、戻り値はint型であるとしてコンパイルを進めます。

したがって、呼び出される前に定義されている関数と戻り値がint型の関数はプロトタイプ宣言をしなくても使えますが、それ以外の関数はプロトタイプ宣言が必要になります。


#geshi(c){{
void inc(int i);  // プロトタイプ宣言

int main(void) {
  int i = 0;
  inc(i);  // 変数を渡す
  printf("%d\n", i);
  return 0;
}


/*
 *  受け取った引数の値を1増やして出力する
 */
void inc(int i) {{
  i++;
  printf(">> %d\n", i)
}
}}



*ポインターによる参照渡し(おまけ) [#z4e2a0a3]

C言語の参照渡しは、''ポインター型''の引数に、変数を記憶している領域の''アドレス''を渡すことによって行います。

ポインターとアドレスについては、C言語応用で勉強する内容なので、ここでは詳しくは説明しません。
#geshi(c){{
/*
 * 参照渡しで受け取った値を1増やす
 */
void inc(int *i) {
  (*i)++;
  printf(">> %d\n", i);
  printf(">> %d\n", *i);
}


int main(void) {
  int i = 0;
  inc(&i);  // 変数 i のアドレスを渡す
  printf("%d\n", i);
  return 0;
}
}}
#geshi(sh){{
luna% a.out
>> 1
1
}}



----
*まとめ [#d7dc0da7]

''値渡し''では、引数を格納するための記憶領域が新たに確保され、そこに引数の値が代入されます。
値渡しでは、受け取った引数の値を変更しても、引数として渡した変数の値は変更されません。
基本型の変数を引数として関数に渡すと値渡しになります。

*課題・練習問題 [#z3bcf410]
**13B-1 [#x75c0e9b]
''参照渡し''では、変数を格納している記憶領域のアドレスが引数として渡されます。
参照渡しでは、受け取ったアドレスに格納されている値を変更すると、引数として私た変数の値も変更されます。
配列を引数として渡すと参照渡しになります。




----
*練習問題 [#z3bcf410]

練習問題は[[こちら>授業/C言語基礎/値渡しと参照渡し/練習問題]]。

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS