構造体は配列でない変数と同じように代入を行ったり, 関数の引数として用いることができる.
#include <stdio.h> #include <stdlib.h> double func(double a) { double ret; ret = a + 1.0; return ret; } int main(void) { double a = 2.0; double b, c; b = a; printf("b = %f\n", b); c = func(a); printf("c = %f\n", c); return EXIT_SUCCESS; }
次のプログラムでは struct COMPLEX が 上のプログラムの doubleと同じように扱われていることに注意.
#include <stdio.h> #include <stdlib.h> struct COMPLEX { double x; double y; }; struct COMPLEX func(struct COMPLEX a) { struct COMPLEX ret; ret.x = a.x + 1.0; ret.y = a.y + 1.0; return ret; } int main(void) { struct COMPLEX a = {10.0, 20.0}; struct COMPLEX b, c; b = a; printf("b = (%f, %f)\n", b.x, b.y); c = func(a); printf("c = (%f, %f)\n", c.x, c.y); return EXIT_SUCCESS; }
[課題1] 複素数の和, 積を求める関数を追加し動作を確認できるようmainを変更せよ。
関数の引数をアドレスにする場合も, double 型と struct COMPLEX型が等価である.
#include <stdio.h> #include <stdlib.h> void func(double *a) { *a += 1.0; } int main(void) { double a = 2.0; printf("a = %f\n", a); func(&a); printf("a = %f\n", a); return EXIT_SUCCESS; }
#include <stdio.h> #include <stdlib.h> void swap(double *a, double *b) { double tmp; tmp = *a; *a = *b; *b = tmp; } int main(void) { double x = 2.0; double y = 4.0; printf("(x, y) = (%f, %f)\n", x, y); swap(&x, &y); printf("(x, y) = (%f, %f)\n", x, y); return EXIT_SUCCESS; }
#include <stdio.h> #include <stdlib.h> struct COMPLEX { double x; double y; }; // (*a).x の部分は a->x と表記できる. void func(struct COMPLEX *a) { (*a).x += 1.0; (*a).y += 2.0; } int main(void) { struct COMPLEX a = {10.0, 20.0}; printf("a = (%f, %f)\n", a.x, a.y); func(&a); printf("a = (%f, %f)\n", a.x, a.y); return EXIT_SUCCESS; }
[課題2] 上のプログラムを参考にして, 二つの複素数のを入れ替えるプログラムを作成せよ.
typedefにより定義しなおすと, double型や int型などの予め用意された型の変数と同様に, 自分で定義した構造体を扱うことができる.
#include <stdio.h> #include <stdlib.h> struct COMPLEX { double x; double y; }; typedef struct COMPLEX complex; void func(complex *a) { (*a).x += 1.0; (*a).y += 2.0; } int main(void) { complex a = {10.0, 20.0}; printf("a = (%f, %f)\n", a.x, a.y); func(&a); printf("a = (%f, %f)\n", a.x, a.y); return EXIT_SUCCESS; }
構造体の配列はdouble型の配列と同じように代入できる.
#include <stdio.h> #include <stdlib.h> #define N 5 int main(void) { int i; double a[N] = {1.0, 2.0, 3.0, 4.0, 5.0}; double b[N]; // 直接 b = a のように代入はできないので各要素を代入する. for(i=0; i<N; i++) b[i] = a[i]; for(i=0; i<N; i++) printf("%f ", b[i]); printf("\n"); return EXIT_SUCCESS; }
#include <stdio.h> #include <stdlib.h> struct COMPLEX { double x; double y; }; typedef struct COMPLEX complex; #define N 5 int main(void) { int i; complex a[N] = {{1.1, 1.2}, {2.1, 2.2}, {3.1, 3.2}, {4.1, 4.2}, {5.1, 5.2}}; complex b[N]; // 直接 b = a のように代入はできないので各要素を代入する. for(i=0; i<N; i++) b[i] = a[i]; for(i=0; i<N; i++) printf("(%f, %f) ", b[i].x, b[i].y); printf("\n"); return EXIT_SUCCESS; }
配列を受け取る関数.
#include <stdio.h> #include <stdlib.h> #define N 5 // アドレスを受け取る void func(double *x) { int i; for(i=0; i<N; i++) x[i] += 0.3; } void double_print(double *x) { int i; for(i=0; i<N; i++) printf("%f ", x[i]); printf("\n"); } int main(void) { double a[N] = {1.0, 2.0, 3.0, 4.0, 5.0}; // 配列名a は &a[0] すなわち aの先頭アドレスを表すことになっている. double_print(a); func(a); // aの先頭アドレスが渡される。 double_print(a); return EXIT_SUCCESS; }
#include <stdio.h> #include <stdlib.h> struct COMPLEX { double x; double y; }; typedef struct COMPLEX complex; #define N 5 // アドレスを受け取る void func(complex *p) { int i; for(i=0; i<N; i++){ p[i].x += 0.2; p[i].y += 0.4; } } int main(void) { int i; complex a[N] = {{1.1, 1.2}, {2.1, 2.2}, {3.1, 3.2}, {4.1, 4.2}, {5.1, 5.2}}; // 配列名a は &a[0] すなわち aの先頭アドレスを表すことになっている. // a の値を表示 (1) for(i=0; i<N; i++) printf("(%f, %f) ", a[i].x, a[i].y); printf("\n"); func(a); // aの先頭アドレスが渡される。 // a の値を表示 (2) for(i=0; i<N; i++) printf("(%f, %f) ", a[i].x, a[i].y); printf("\n"); return EXIT_SUCCESS; }
[課題3] 上のプログラムの 「a の値を表示」している部分2ヶ所を関数化せよ.
[課題4] 以下のプログラムを参考にして, 大きさが最大のベクトルを返す関数とその動作が確認できる main 関数を作成せよ.
ベクトルの長さを求めるプログラム:
#include <stdio.h> #include <stdlib.h> #include <math.h> struct VECTOR { char label[50]; double x; double y; double z; }; typedef struct VECTOR vector; #define N 3 double func(vector p) { double r; r = sqrt( p.x * p.x + p.y * p.y + p.z * p.z ); return r; } int main(void) { int i; vector a = {"A", 10.0, 20.0, 30.0}; vector b[N] = { {"B1", 1.0, 2.0, 3.0}, {"B2", 1.1, 2.1, 3.1}, {"B3", 1.2, 2.2, 3.2}, }; printf("%sの長さ: %f\n", a.label, func(a)); for(i=0; i<N; i++) printf("%sの長さ: %f\n", b[i].label, func(b[i])); return EXIT_SUCCESS; }
ベクトルの和 (合成ベクトル) を求めるプログラム:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> struct VECTOR { char label[50]; double x; double y; double z; }; typedef struct VECTOR vector; vector add(vector p, vector q) { vector r; // sprintf は書式に従って第1引数の文字列に代入する関数. sprintf(r.label, "%s + %s", p.label, q.label); r.x = p.x + p.x; r.y = p.y + p.y; r.z = p.z + p.z; return r; } void vector_print(vector p) { printf("%s = (%f, %f, %f)\n", p.label, p.x, p.y, p.z); } int main(void) { vector a = {"A", 10.0, 20.0, 30.0}; vector b = {"B", 1.0, 2.0, 3.0}; vector c; vector_print(a); vector_print(b); c = add(a, b); vector_print(c); return EXIT_SUCCESS; }
[課題5] 2点間の距離を求めるプログラムを作成せよ.
[課題6] 2つのベクトルがなす角を求めるプログラムを作成せよ.
[課題7] 次に示すように構造体のメンバーとして配列を有する場合, 代入はどのように行われるか. また関数の引数としての渡すとき, どのように渡されるかを, double 型の変数と比較して答えよ。
struct DATA { double x[100]; double y[100]; int n; // データ点の数 };
[課題8] 次に示すように構造体のメンバーとしてポインタ変数を有する場合について 課題8と同様の視点から double 型と比較せよ.
struct DATA { double *x; double *y; int n; // データ点の数 };
struct DATA を用いた例:
#include <stdio.h> #include <stdlib.h> struct DATA { double *x; double *y; int n; }; typedef struct DATA data; #define N 100 void data_print(data *d) { int i; for(i=0; i < d->n; i++) printf("(%f, %f)\n", d->x[i], d->y[i]); } int main(void) { int i; double x[N], y[N]; data d; // 初期化 d.x = x; // 領域の確保された配列を指すようにすること. d.y = y; d.n = N; // y = x*x であるデータを代入 for(i=0; i<N; i++){ d.x[i] = (double)i; d.y[i] = d.x[i] * d.x[i] ; } data_print(&d); return EXIT_SUCCESS; }
#include <stdio.h> #include <stdlib.h> struct DATA { double *x; double *y; int n; }; typedef struct DATA data; #define N 100 void data_init(data *d, double x, double y, int n) { d->x = x; d->y = y; d->n = n; } void data_print(data *d) { int i; for(i=0; i < d->n; i++) printf("(%f, %f)\n", d->x[i], d->y[i]); } void data_set(data *d) { int i; for(i=0; i < d->n; i++){ d->x[i] = (double)i; d->y[i] = d->x[i] * d->x[i] ; } } int main(void) { double x[N], y[N]; // 領域の確保が必要. data d; data_init(&d, x, y, N); // 初期化 data_set(&d); // y = x*x であるデータを代入 data_print(&d); // データの値を表示 return EXIT_SUCCESS; }
[課題9] 上記2種類の構造体について, const で修飾された引数として関数に渡したとき, const が及ぶ範囲を答えよ.
(gcc の場合, const で修飾された変数に代入を行おうとするとコンパイラーがメッ セージを出力するので, それを基準とするとよい.)