見出し画像

C言語教室 第2回 - 参照渡し

前回、関数を呼び出す時に、呼び出した関数の中で使う値を呼び出し側で引き数という形で渡すことができるというのをやりました。

C言語教室 第1回 - 関数呼び出し

今回は、呼び出した関数で引き数で渡した値を変更できる方法を覚えます。

引き数で渡した変数の値を「入れ替える」関数を書いてみましょう。関数を呼び出さずにmainでそのままやるのであれば、以下のようになるはずです。

#include <stdio.h>

void main() {
  int a;
  int b;
  int z;

  a = 2;
  b = 9;
  printf("a=%d,b=%d\n", a, b);

  z = a;
  a = b;
  b = z;
  printf("a=%d,b=%d\n", a, b);
}
a=2,b=9
a=9,b=2

ちゃんと入れ替わりましたね。これを関数の形で書けば

#include <stdio.h>

void swap(int a, int b) {
  int c;

  c = a;
  a = b;
  b = c;
}

void main() {
  int a;
  int b;

  a = 2;
  b = 9;
  printf("a=%d,b=%d\n", a, b);

  swap(a, b);
  printf("a=%d,b=%d\n", a, b);
}

あれ?入れ替わっていませんね。

a=2,b=9
a=2,b=9

mainのa,bとswapのa,bは別の変数なので、swapの中で値を変更しても、mainのa,bには関係がないのです。

関数を呼び出すときの引き数は、呼び出した関数の変数に「コピー」される。

お約束1

つまりコピーしたものを変更しても、元の値はそのままです。では呼び出した関数の変数の値を変えることはできないのかというと、そのための方法があるのです。

どのようにするのかというと、関数を呼び出す時にコピーされるのですから、コピーされるものを「値」ではなくて「値のあるところ」に変えるのです。変数と言うのはメモリのどこかに値があるわけで、この「どこか」を’&’という演算子で得ることができます。逆に「どこか」から「値」に戻すには’*’という演算子を使います。

#include <stdio.h>

void main() {
  int a;
  int *pa;

  a = 1;
  printf("a=%d\n", a);

  pa = &a;
  *pa = 2;
  printf("a=%d\n", a);
}
a=1
a=2

「値のあるところ」を使って、値を変えることができました。この「値のあるところ」を変数にするときには、変数を宣言する時に名前の前に’*’をつけます(int vを指すのであれば int *pv)。

変数の「値のあるところ」は’&’、「値のあるところ」から値を得るには’*’を使います。

お約束2

「値のあるところ」の変数を宣言するときには、値の型の後ろに(変数名の前)’*’をつけます。

お約束3

これを使って値を入れ替える関数を書きます。「値のあるところ」をコピーしても、それが指すものは同じなので、呼び出した関数の元の値を変えられます。

#include <stdio.h>

void swap(int *px, int *py) {
  int z;

  z = *px;
  *px = *py;
  *py = z;
}

void main() {
  int a;
  int b;

  a = 2;
  b = 9;
  printf("a=%d,b=%d\n", a, b);
  
  swap(&a, &b);
  printf("a=%d,b=%d\n", a, b);
}
a=2,b=9
a=9,b=2

この「値のあるところ」のことを一般的には「参照」とか「リファレンス」などと呼ぶのですが、C言語の場合は「ポインタ」と言います。

ご存じの方には当たり前でしょうが、実はC言語のポインタはメモリのアドレスです。そのため&をつけることをアドレスを取るなどということもあります。C言語の場合、ポインタ変数に自由な値を入れることができるので、ポインタの指す先が本当に変数なのかは、きちんとチェックしているわけではありません。そのためプログラムを書く人がポインタが正しく思っている場所を指していることをチェックする必要があります。

なおアドレスを取ることが出来るのは変数だけなので、変数ではなくて’2’などの数値に対するアドレスを取ることはできません(swap(&a, &2)とかはできない)。

このポインタを使いこなすことこそC言語の醍醐味で、非常に便利な機能なのですが、なにせ指す先を管理するのはプログラムを書く人なので、ここを間違えてバグったり、プログラムが不意に停止したりすることは、とてもよく起きます。他の言語ではポインタを自由に設定できないようにすることで、バグを起きにくくしていることが多いですが、基本的な考え方はC言語と変わりません。

次は配列に進みます。


課題

引き数で与えた変数の値を2倍にする関数を書いてください。


第1回の課題の回答例

引き数の値の合計を返す関数sumを書く

#include <stdio.h>

int sum(int x, int y) {
  int s;

  s = x + y;
  return s;
}

void main() {
  int a;
  int b;
  int s;

  a = 2;
  b = 9;
  s = sum(a, b);
  printf("Sum(%d,%d)=%d\n", a, b, s);
}
Sum(2,9)=11

引き数の値の平均値を小数で返す関数にする

#include <stdio.h>

double ave(int x, int y) {
  double v;

  v = (x + y) / 2.0;
  return v;
}

void main() {
  int a;
  int b;
  double v;a = 2;

  b = 9;
  v = ave(a, b);
  printf("Ave(%d,%d)=%f\n", a, b, v);
}
Ave(2,9)=5.500000

aveの中で割り算の数字を’2’としてハマった人はいませんか?

いずれも例なので、少し違う書き方にすることもできます。

この記事が気に入ったらサポートをしてみませんか?