関数への(複数の)値の受け渡し(written by Taku Nakahara)
前回は、関数を作成して、それに対してひとつの変数を渡したり(引数)、受け取ったり(返り値)しました。
今回は、複数の値を渡したり、受け取ったりする方法をやろうと思います。
まず、複数個の値を渡す方法ですが、これは簡単です。
(shurrup_full_name.c)
#include <stdio.h> void func(char *, char *); int main(int argc, char *argv[]) { char *lastname, *familyname; familyname = argv[1]; lastname = argv[2]; func(familyname, lastname); return 0; } void func(char *sei, char *mei) { printf("shurrup %s %s!\n", sei, mei); }
しれっとポインタを使っていますが、まあ特にコメントはしません。なんとなく分かると思います。
次に、複数個の値を返してもらう方法ですが、return制御文ではひとつの値しか返すことができません。複数の値を返してもらうためには、ポインタを渡して、そのアドレスにある実体そのものを変更するという方法をとります。例によって理屈ぬきでちょっと実験してみましょう。
(multivars1.c)
#include <stdio.h> void pass_vars(int, int); int main() { int v1 = 100; int v2 = 200; printf("initial values in main()\tv1=%d\tv2=%d\n", v1, v2); pass_vars(v1, v2); printf("processed values in main()\tv1=%d\tv2=%d\n", v1, v2); return 0; } void pass_vars(int v1, int v2) { printf("initial values in pass_vars()\tv1=%d\tv2=%d\n", v1, v2); v1 = 300; v2 = 400; printf("processed values in pass_vars()\tv1=%d\tv2=%d\n", v1, v2); }
実行結果は以下の通り
initial values in main() v1=100 v2=200 initial values in pass_vars() v1=100 v2=200 processed values in pass_vars() v1=300 v2=400 processed values in main() v1=100 v2=200
この結果を見ると、main()関数から渡した値をmain()関数内からprintfすると、まったく変わっていません。しかし、呼び出し関数内でprintfした時には確かに変化していたようです。これはどういうことでしょうか?次のプログラムを実行してみてください。
(multivars2.c)
#include <stdio.h> void pass_vars(int, int); void pass_address(int *, int *); int main() { int v1 = 100; int v2 = 200; printf("initial values in main()\tv1=%d\tv2=%d\n", v1, v2); pass_vars(v1, v2); printf("values after pass_vars in main()\tv1=%d\tv2=%d\n", v1, v2); pass_address(&v1, &v2); printf("values after pass_address in main()\tv1=%d\tv2=%d\n", v1, v2); return 0; } void pass_vars(int v1, int v2) { printf("initial values in pass_vars()\tv1=%d\tv2=%d\n", v1, v2); v1 = 300; v2 = 400; printf("processed values in pass_vars()\tv1=%d\tv2=%d\n", v1, v2); } void pass_address(int *v1, int *v2) { printf("initial values in pass_address()\tv1=%d\tv2=%d\n", *v1, *v2); *v1 = 300; *v2 = 400; printf("processed values in pass_address()\tv1=%d\tv2=%d\n", *v1, *v2); }
実行結果は
initial values in main() v1=100 v2=200 initial values in pass_vars() v1=100 v2=200 processed values in pass_vars() v1=300 v2=400 values after pass_vars in main() v1=100 v2=200 initial values in pass_address() v1=100 v2=200 processed values in pass_address() v1=300 v2=400 values after pass_address in main() v1=300 v2=400
こちらのプログラムではmain()関数内でprintfをすると、pass_address()関数を呼び出した前と後で、与えた値が変わっています。なぜこういう動作をしたのかを理解するには、ポインタについて理解する必要があります。が、理屈をこねるよりも実際にプログラム中でどう使われているかを理解するほうがいいでしょう。
まず、関数pass_address()をmain()中で呼び出す際に
pass_address(&v1, &v2);
とやっています。&v1というのは、変数v1の「アドレス」を意味します。main()関数の中でv1の「値」は100ですが、この値がおいてあるメモリ上の住所が「アドレス」です。つまりmain()関数がpass_address()関数を呼び出して仕事を発注するときには100とか200とかの具体的な値を渡すのではなくて、「v1の住所がここで、v2の住所がここですよ。よろしく」という感じで、処理してほしい値のある「住所」を指定しています。
では、受け取るほうはどう受け取っているかというと、
void pass_address(int *v1, int *v2) { … }
という感じで受け取っています。int *v1というのは、int型の*v1という変数ではなくて「int型のアドレスの入る」ポインタ型の変数*v1ということを意味します。main()の中から投げられた&v1というアドレスはpass_address()関数では*v1に代入されます。*v1は単なるアドレスではなくそのアドレスの指定する実体を指し示す(pointする)もの(= pointer)なので、ポインタ変数を操作することは、そのポインタの指し示す実体を操作することと等価です。なので
*v1 = 300;
とやると、*v1で指し示しているアドレス(=これはmain()中のv1のアドレスと同じ)にある変数の実体を300にするということになります。
最後に、簡単な関数間で値を受け渡す(共有する)プログラムをこれ以上ないくらいに回りくどく説明して終わりにします。
(pointerMechanism.c)
#include <stdio.h> void function(int, int *); int main() { int c1 = 100; int c2 = 120; printf("address: &c1 = %p &c2 = %p\n", &c1, &c2); printf("value before function: c1 = %d c2 = %d\n", c1, c2); function(c1, &c2); printf("value after function: c1 = %d c2 = %d\n", c1, c2); } void function(int a, int *b) { printf("address: &a = %p &b = %p\n", &a, &b); printf("value: a = %d b = %p\n", a, b); printf("initial real value in function: a = %d *b = %d\n", a, *b); a *= 2; *b *= 2; printf("processed real value in function: a = %d *b = %d\n", a, *b); }
この実行結果が
address: &c1 = 0xbffff5b8 &c2 = 0xbffff5bc value before function: c1 = 100 c2 = 120 address: &a = 0xbffff598 &b = 0xbffff59c value: a = 100 b = 0xbffff5bc initial real value in function: a = 100 *b = 120 processed real value in function: a = 200 *b = 240 value after function: c1 = 100 c2 = 240
アドレスは、実行のたびに異なった場所に設定されるので、毎回違う値が出ます。このプログラムで変数のアドレスや実体がどのように変化したのかのフローが以下のようになります。
このようにして関数間ではアドレスを共有することによって複数の返り値を得ることができます。今日はこれで終わりです。