#include <stdio.h> void complex_add(double are, double aim, double bre, double bim, double *cre, double *cim) { *cre = are + bre; *cim = aim + bim; } void complex_mul(double are, double aim, double bre, double bim, double *cre, double *cim) { *cre = are * bre - aim * bim; *cim = are * bim + aim * bre; } void complex_print(double are, double aim) { printf("(%f)+(%f)i", are, aim); } int main(void) { double xre, xim, yre, yim, tmpre, tmpim; scanf("%lf", &xre); scanf("%lf", &xim); complex_mul(xre, xim, xre, xim, &tmpre, &tmpim); complex_add(tmpre, tmpim, xre, xim, &yre, &yim); complex_print(yre, yim); printf("\n"); return 0; } |
複素数なので、実部 (real part) と虚部 (imaginary part) が必ずペアで 現れていることが分かる。しかし、変数を2つ用意し別々に宣言や受渡しを 行っており、見通しが悪い。 構造体を使うと、両者が一組であるということをより分かりやすく表現する ことが出来る。
#include <stdio.h> struct complex { double re; double im; }; void complex_add(struct complex a, struct complex b, struct complex *c) { (*c).re = a.re + b.re; (*c).im = a.im + b.im; } void complex_mul(struct complex a, struct complex b, struct complex *c) { (*c).re = a.re * b.re - a.im * b.im; (*c).im = a.re * b.im + a.im * b.re; } void complex_print(struct complex a) { printf("(%f)+(%f)i", a.re, a.im); } int main(void) { struct complex x, y, tmp; scanf("%lf", &(x.re)); scanf("%lf", &(x.im)); complex_mul(x, x, &tmp); complex_add(tmp, x, &y); complex_print(y); printf("\n"); return 0; } |
また、構造体にすれば「一つの変数」なので戻り値を直接返せるので、 次のようにより簡潔に書くことも出来る。
#include <stdio.h> struct complex { double re; double im; }; struct complex complex_add(struct complex a, struct complex b) { struct complex c; c.re = a.re + b.re; c.im = a.im + b.im; return c; } struct complex complex_mul(struct complex a, struct complex b) { struct complex c; c.re = a.re * b.re - a.im * b.im; c.im = a.re * b.im + a.im * b.re; return c; } void complex_print(struct complex a) { printf("(%f)+(%f)i", a.re, a.im); } int main(void) { struct complex x, y, tmp; scanf("%lf", &(x.re)); scanf("%lf", &(x.im)); tmp = complex_mul(x, x); y = complex_add(tmp, x); complex_print(y); printf("\n"); return 0; } |
以下、これをサンプルとして、構造体の使い方を説明する。
struct タグ名 { 型名 メンバ名; 型名 メンバ名; ... };のように書く。例えば、
struct complex { double re; double im; };のように書ける。異なる型が混在していてもよい。例えば、
struct person { char name[100]; int age; double height; double weight; };のように書くことが出来る。
構造体の定義は、「タグ名」の構造体がどのような構造を持つかを 定義したに過ぎず、これだけでは実際のメモリ領域は割り当てられていない。
struct タグ名 変数名;のように宣言する。例えば、
struct complex x;と書く。普通の変数と同様に、宣言と同時に初期化することも出来る。例えば、
struct complex x = {1.0, 2.0};中身を取り出すときは、
変数名.メンバ名のように書く。例えば、x.re、x.imなど。
構造体をポインタで渡すと、
(*x).reのような記述がどうしても多くなるが、これに関しては、
x->reのような略記法がある。例えば上の例のcomplex_add関数は、
void complex_add(struct complex a, struct complex b, struct complex *c) { c->re = a.re + b.re; c->im = a.im + b.im; }と書いてもよい。
いわゆるsyntax sugar, syntactic sugar (糖衣構文)の一種。
typedef 型名 型の別名;という使い方。例えば、
typedef unsigned int uint; main() { unsigned int a; uint b; }のように使う。uintという名前が、unsigned intの別名として使えるようになる。
これを利用して、構造体に別名を付けて使うことが多いので、例として挙げておく。
#include <stdio.h> typedef struct { double re; double im; } complex; complex complex_add(complex a, complex b) { complex c; c.re = a.re + b.re; c.im = a.im + b.im; return c; } complex complex_mul(complex a, complex b) { complex c; c.re = a.re * b.re - a.im * b.im; c.im = a.re * b.im + a.im * b.re; return c; } void complex_print(complex a) { printf("(%f)+(%f)i", a.re, a.im); } int main(void) { complex x, y, tmp; scanf("%lf", &(x.re)); scanf("%lf", &(x.im)); tmp = complex_mul(x, x); y = complex_add(tmp, x); complex_print(y); printf("\n"); return 0; } |
つまり、構造体にcomplexという別名を付けることによって、使うときに いちいちstructと書かなくて済むようになる。
先頭の宣言部は本来は
typedef struct complex { double re; double im; } complex;だが、この場合別名が付けられたことによって構造体を特定するための タグが不要になるため、タグ名を省略している。