その前に、typedef しておきましょう。先ほどの struct の型定義と同時に、 typedef することができます。 これで、 struct complexと書く代りに complex_tでOKですし、 complex_tp で、struct complexへのpointerという型を指すことになります。
typedef struct complex { double real; double imag; } complex_t, * complex_tp;結構便利なので、僕は構造体をつくると必ず typedef も一緒にするようにしています。
さて、準備も整ったところで、実際に関数をつくってみましょう(struct_B.c)。 つくるのは、複素数の足し算を行う関数です。 まずは、complex_t add_by_value(complex_t x, complex_t y);です。
complex_t add_by_value(complex_t x, complex_t y) { complex_t result; printf("add_by_value: x real %f imag %f address %x\n", x.real, x.imag, &x); printf("add_by_value: r real %f imag %f address %x\n", result.real, result.imag, &result); result.real = x.real + y.real; result.imag = x.imag + y.imag; return result; }struct を 2 個引数にとり、返り値も struct という関数です。 C では、引数を局所変数の一つと思ってくれて問題ありません。 つまり、呼出し側(caller)の struct と、呼び出された側(callee) の struct は同一物ではありません。 関数呼出しの際は、コピーをつくって呼び出された側(callee)に渡されます。 だから実行結果の x, a のアドレスを見比べると異なることに気づくはず。
/* 呼出し側コード */ printf("a real %f imag %f address %x\n", a.real, a.imag, &a); printf("c real %f imag %f address %x\n", c.real, c.imag, &c); c = add_by_value(a,b); printf("c after real %f imag %f address %x\n", c.real, c.imag, &c); /* 実行サンプル結果 a real 1.000000 imag 0.000000 address ffbef530 c real -1.000000 imag -1.000000 address ffbef510 add_by_value: x real 1.000000 imag 0.000000 address ffbef4f0 add_by_value: r real 0.000000 imag 0.000000 address ffbef460 c after real 3.000000 imag 1.500000 address ffbef510 */
さて、次は、pointer値を渡すようにしましょう。 まずは、void add_by_reference(complex_tp result, complex_tp x, complex_tp y);です。 x と y を足して、 result に格納します。 関数自体はこんな感じ。
void add_by_reference(complex_tp result, complex_tp x, complex_tp y) { printf("add_by_refer: x real %f imag %f address %x\n", x->real, x->imag, x); printf("add_by_refer: y real %f imag %f address %x\n", y->real, y->imag, y); printf("add_by_refer: r real %f imag %f address %x\n", result->real, result->imag, result); result->real = x->real + y->real; result->imag = x->imag + y->imag; return; } /* 呼出し側コード printf("d real %f imag %f address %x\n", d.real, d.imag, &d); add_by_reference(&d, &a, &b); printf("d after real %f imag %f address %x\n", d.real, d.imag, &d); */この場合、呼び出された側(callee)にわたるのは、&a, &b, &d という参照のみです。callee では、 参照を介してデータにアクセスすることで、内容を書き換えています。 というわけで、実行結果を眺めてもアドレスは共通です。
/* 実行サンプル結果 a real 1.000000 imag 0.000000 address ffbef530 d real -2.000000 imag -2.000000 address ffbef500 add_by_refer: x real 1.000000 imag 0.000000 address ffbef530 add_by_refer: r real -2.000000 imag -2.000000 address ffbef500 d after real 3.000000 imag 1.500000 address ffbef500*/
一方で、データをためておく場所で、呼び出された側でデータを更新して、 呼び出した側でその更新内容を利用するといった場合は、データの場所を伝えた方が素直でしょう。 データへの参照を渡すため、call by reference的です。
2001.11.14/ Tomio KAMADA: kamada@cs.kobe-u.ac.jp