以下の事柄を理解すること.
構造体の例
// 線分 struct SEGMENT { double x1, y1; // 始点 double x2, y2; // 終点 char color[10]; // 色 }; // 始点終点を配列で表現した線分 // 始点を(x[0], y[0]), 終点を(x[1], y[1])とする. struct SEGMENT { double x[2]; // 始点 double y[2]; // 終点 char color[10]; // 色 }; // 円 struct CIRCLE { double x, y; // 中心 double r; // 半径 char color[10]; // 色 }; // 四角形 // 頂点は (x[0], y[0]), (x[1], y[1]), (x[2], y[2]), (x[3], y[3]) とする. struct RECTANGLE { double x[4]; double y[4]; char color[10]; }; // 本 struct BOOK { char title[100]; char author[50]; char publisher[50]; // 出版社 int year; // 出版年 int price; // 価格 }; // 都市 struct CITY { char name[50]; // 名前 double area; // 面積 int population; // 人口 double altitude; // 標高 }; // 部屋 struct ROOM { char building[20]; // 建物 int number; // 部屋番号 int capacity; // 収容人数 };
配列, 配列でない変数, 構造体を比較し, 整理して理解しておくこと.
program A (See program 1.)
#include <stdio.h> // [A] double 型の定義は予めされている. int main(void) { double x; // [B] double 型の宣言 (記憶域上での領域の確保) x = 3; // [C] 代入 printf("%f\n", x); // [D] 参照 return 0; }
program B (See (programs 6) and program 7.)
#include <stdio.h> int main(void) { double x; double *y; // ポインタ変数 x = 3; y = &x; // xのアドレスを代入 // [C] y が指すアドレスにある内容を「*」を用いて参照 printf("%f\n", *y); return 0; }
program C (See program 8.)
#include <stdio.h> // double 型を受け取る関数 void double_print(double a) { printf("%f\n", a); } int main(void) { double x = 3; double_print(x); return 0; }
program D (program 10.)
#include <stdio.h> // double 型を返す関数 double double_func(void) { double a; a = 3; return a; } int main(void) { double x; x = double_func(); // 関数の戻り値をxに代入. printf("%f\n", x); return 0; }
program E (See (program 13) and program 15.)
#include <stdio.h> // double 型ポインタ変数 (double型の変数/定数のアドレス) を受け取る関数 void double_func(double *a) { printf("(2) %f\n", *a); // 関数の中. *a = 30.0; // main 中の x の領域に代入. } int main(void) { double x = 3.0; printf("(1) %f\n", x); // 呼び出し前 double_func(&x); // アドレスを渡す. printf("(3) %f\n", x); // 呼び出し後. 値が変わることに注意. return 0; }
例えば次のようなベクトルを扱うプログラムを考えてみます.
#include <stdio.h> #include <stdlib.h> #include <string.h> // 最大の次元数. // C99 が使える環境では, ここの#define の行は // const int nmax = 10; // のように定数にする方がよい. #define NMAX 10 #define LABELMAX 100 int main(void) { int i; int xn; double xval[NMAX]; char xlabel[LABELMAX]; int yn; double yval[NMAX]; char ylabel[LABELMAX]; xn = 3; // xの次元. xn は NMAX と同じかまたは小さいこと. for(i=0; i<xn; i++) // 成分の代入 xval[i] = (double)i; strcpy(xlabel, "x"); // 文字列 xlabel に 文字列 "x" をコピー // y に x を代入. yn = xn; for(i=0; i<yn; i++) yval[i] = xval[i] + 10; strcpy(ylabel, "y"); // y を表示 printf("%s = ( ", ylabel); for(i=0; i<yn; i++) printf("%g ", yval[i]); printf(")\n"); return EXIT_SUCCESS; }
program 1
#include <stdio.h> #include <string.h> // strcpyを用いるときインクルードする. /* strcpy は 文字列をコピーする関数 #include <string.h> char destination[100]; char source[100] = "ABC"; strcpy(destination, source); とすると source の文字列が destination にコピーされる. */ // [A] 構造体の定義 struct VECTOR { int dimension; double val[10]; char label[100]; }; int main(void) { struct VECTOR x; // [B] VECTOR 型の宣言 (記憶域上での領域の確保) // [C] 構造体 x の各メンバーへの代入 x.dimension = 3; // 整数の場合 x.val[0] = 10.0; // 配列の場合 x.val[1] = 20.0; x.val[2] = 30.0; strcpy(x.label, "vector x"); // 文字列の場合. // [D] x のメンバーの参照 printf("%s\n", x.label); printf("%d\n", x.dimension); printf("%f\n", x.val[0]); printf("%f\n", x.val[1]); printf("%f\n", x.val[2]); return 0; }
program 2
#include <stdio.h> #include <stdlib.h> #include <string.h> #define NMAX 10 #define LABELMAX 100 struct VECTOR { int n; double val[NMAX]; char label[LABELMAX]; }; int main(void) { int i; struct VECTOR x, y; // 構造体の各メンバーへの代入 x.n = 3; strcpy(x.label, "x"); // strcpy は 文字列 x.label に 文字列"x"書き込む関数. for(i=0; i<x.n; i++) x.val[i] = (double)i; // y に x + 10 を代入. y.n = x.n; strcpy(y.label, "y"); for(i=0; i<y.n; i++) y.val[i] = x.val[i] + 10; // y を表示 printf("%s = ( ", y.label); for(i=0; i<y.n; i++) printf("%g ", y.val[i]); printf(")\n"); return EXIT_SUCCESS; }
program 3
#include <stdio.h> #include <stdlib.h> #include <string.h> #define NMAX 10 #define LABELMAX 100 struct VECTOR { int n; double val[NMAX]; char label[LABELMAX]; }; int main(void) { int i; // 初期化. メンバーが定義された順に行う. struct VECTOR x = {3, {100, 200, 300}, "x"}; // x を表示 printf("%s = ( ", x.label); for(i=0; i<x.n; i++) printf("%g ", x.val[i]); printf(")\n"); return EXIT_SUCCESS; }
program 4
#include <stdio.h> #include <stdlib.h> #include <string.h> #define NMAX 10 #define LABELMAX 100 struct VECTOR { int n; double val[NMAX]; char label[LABELMAX]; }; int main(void) { int i; // 初期化. メンバーが定義された順に行う. struct VECTOR x = {3, {100, 200, 300}, "x"}; struct VECTOR y = {0, {0}, "y"}; // y に x + 10 を代入. y.n = x.n; for(i=0; i<y.n; i++) y.val[i] = x.val[i] + 10; // y を表示 printf("%s = ( ", y.label); for(i=0; i<y.n; i++) printf("%g ", y.val[i]); printf(")\n"); return EXIT_SUCCESS; }
program 5
#include <stdio.h> #include <stdlib.h> #include <string.h> #define NMAX 10 #define LABELMAX 100 struct VECTOR { int n; double val[NMAX]; char label[LABELMAX]; }; int main(void) { int i; struct VECTOR x = {3, {100, 200, 300}, "x"}; struct VECTOR y; // y に x を代入. y = x; // y を表示 printf("%s = ( ", y.label); for(i=0; i<y.n; i++) printf("%g ", y.val[i]); printf(")\n"); return EXIT_SUCCESS; }
program 6
#include <stdio.h> #include <string.h> struct VECTOR { char label[100]; int dimension; double val[10]; }; /* ここでは, 各メンバーに代入したが, 構造体は struct VECTOR x = {"vector x", 3, {100.0, 200.0, 300.0}}; のように初期化できる. */ int main(void) { struct VECTOR x; struct VECTOR *y; // [A] ポインタ変数 strcpy(x.label, "vector x"); x.dimension = 3; x.val[0] = 100.0; x.val[1] = 200.0; x.val[2] = 300.0; y = &x; // [B] xのアドレスを代入 // [C] y が指すアドレスにある内容を「*」を用いて参照 printf("%s\n", (*y).label); // *は優先順位が低いので()が必要 printf("%d\n", (*y).dimension); printf("%g\n", (*y).val[0]); printf("%g\n", (*y).val[1]); printf("%g\n", (*y).val[2]); return 0; }
(*y).n と書くのは面倒なので y->nという別の表記方法が用意されている.
program 7
#include <stdio.h> #include <stdlib.h> #include <string.h> #define NMAX 10 #define LABELMAX 100 struct VECTOR { int n; double val[NMAX]; char label[LABELMAX]; }; int main(void) { int i; struct VECTOR x = {3, {100, 200, 300}, "x"}; struct VECTOR *y; // ポインタ変数 y = &x; // xのアドレスを代入 // y が指すアドレスにある内容を参照 printf("%s = ( ", y->label); // *は優先順位が低いので()が必要 for(i=0; i < y->n ; i++) printf("%g ", y->val[i]); printf(")\n"); return EXIT_SUCCESS; }
program 8
#include <stdio.h> struct VECTOR { int dimension; double val[10]; char label[100]; }; // 構造体を受け取る関数. void vector_print(struct VECTOR a) { printf("%s\n", a.label); printf("%d\n", a.dimension); printf("%f\n", a.val[0]); printf("%f\n", a.val[1]); printf("%f\n", a.val[2]); } int main(void) { struct VECTOR x = {3, {100, 200, 300}, "x"}; vector_print(x); return 0; }
program 9
#include <stdio.h> #include <stdlib.h> #include <string.h> #define NMAX 10 #define LABELMAX 100 struct VECTOR { int n; double val[NMAX]; char label[LABELMAX]; }; // 構造体を受け取る関数. void vector_print(struct VECTOR a) { int i; printf("%s = ( ", a.label); for(i=0; i<a.n; i++) printf("%g ", a.val[i]); printf(")\n"); } int main(void) { struct VECTOR x = {3, {100, 200, 300}, "x"}; struct VECTOR y = {0, {0}, "y"}; y = x; // y に x を代入. vector_print(y); // y を表示 return EXIT_SUCCESS; }
program 10
#include <stdio.h> #include <string.h> struct VECTOR { int n; double val[10]; char label[100]; }; // VECTOR型を返す関数. // 構造体を用いることで, 構造体のメンバー全ての値(複数の値)を返すことができる. struct VECTOR vector_func(void) { struct VECTOR a; strcpy(a.lable, "vector a"); a.dimension = 3; a.val[0] = 100.0; a.val[1] = 200.0; a.val[2] = 300.0; return a; } int main(void) { struct VECTOR x; x = vector_func(); // 関数の戻り値をxに代入. printf("%s\n", x.label); printf("%d\n", x.dimension); printf("%f\n", x.val[0]); printf("%f\n", x.val[1]); printf("%f\n", x.val[2]); return 0; }
program 11
#include <stdio.h> #include <stdlib.h> #include <string.h> #define NMAX 10 #define LABELMAX 100 struct VECTOR { int n; double val[NMAX]; char label[LABELMAX]; }; // 構造体を受け取る関数. void vector_print(struct VECTOR a) { int i; printf("%s = ( ", a.label); for(i=0; i<a.n; i++) printf("%g ", a.val[i]); printf(")\n"); } // 構造体を返す関数. struct VECTOR vector_add(struct VECTOR a, struct VECTOR b) { int i; struct VECTOR tmp; // sprintf は printf の結果を文字列 tmp.label に書き込む関数. sprintf(tmp.label, "%s + %s", a.label, b.label); if ( a.n < b.n ) tmp.n = a.n; else tmp.n = b.n; for(i=0; i< a.n; i++) tmp.val[i] = a.val[i] + b.val[i]; return tmp; // 構造体を用いることで複数の値を返すことができる. } int main(void) { struct VECTOR c10 = {3, {10, 10, 10}, "10"}; struct VECTOR x = {3, {100, 200, 300}, "x"}; struct VECTOR y = {0, {0}, "y"}; y = vector_add(x, c10); // y = x + 10 vector_print(y); // y を表示 return EXIT_SUCCESS; }
呼び出した関数内で引数のメンバーの値を変更しても, 呼び出し側の構造体のメンバーの値は変更されていないことに注意.
program 12
#include <stdio.h> #include <stdlib.h> #include <string.h> #define NMAX 10 #define LABELMAX 100 struct VECTOR { int n; double val[NMAX]; char label[LABELMAX]; }; void vector_func(struct VECTOR v) { int i; // 関数内で引数のメンバーの値を変更. for(i=0; i<v.n; i++) v.val[i] += 1000.0; } void vector_print(struct VECTOR a) { int i; printf("%s = ( ", a.label); for(i=0; i<a.n; i++) printf("%g ", a.val[i]); printf(")\n"); } int main(void) { struct VECTOR x = {3, {100.0, 200.0, 300.0}, "x"}; vector_print(x); vector_func(x); vector_print(x); // x のメンバーの値は変更されていないことに注意. return EXIT_SUCCESS; }
program 13
#include <stdio.h> #include <string.h> struct VECTOR { int dimension; double val[10]; char label[100]; }; // VECTOR型ポインタ変数 (VECTOR型の変数/定数のアドレス) を受け取る関数 void vector_func(struct VECTOR *a) { // 関数の中 printf("(2) %s\n", (*a).label); printf("(2) %d\n", (*a).dimension); printf("(2) %f\n", (*a).val[0]); printf("(2) %f\n", (*a).val[1]); printf("(2) %f\n", (*a).val[2]); // main 中のxに代入. strcpy((*a).label, "vector a"); (*a).dimension = 3; (*a).val[0] = 1000.0; (*a).val[1] = 2000.0; (*a).val[2] = 3000.0; } int main(void) { struct VECTOR x = {3, {100.0, 200.0, 300.0}, "x"}; // 呼び出し前 printf("(1) %s\n", x.label); printf("(1) %d\n", x.dimension); printf("(1) %f\n", x.val[0]); printf("(1) %f\n", x.val[1]); printf("(1) %f\n", x.val[2]); vector_func(&x); // アドレスを渡す. // 呼び出し後 printf("(3) %s\n", x.label); printf("(3) %d\n", x.dimension); printf("(3) %f\n", x.val[0]); printf("(3) %f\n", x.val[1]); printf("(3) %f\n", x.val[2]); return 0; }
program 14
#include <stdio.h> #include <stdlib.h> #include <string.h> #define NMAX 10 #define LABELMAX 100 struct VECTOR { int n; double val[NMAX]; char label[LABELMAX]; }; // アドレスを受け取る関数. // v はポインタ変数. void vector_func(struct VECTOR *v) { int i; // 関数内で引数のメンバーの値を変更. for ( i = 0; i < (*v).n; i++ ) (*v).val[i] += 1000.0; } void vector_print(struct VECTOR *a) { int i; printf("%s = ( ", (*a).label); for(i=0; i<(*a).n; i++) printf("%g ", (*a).val[i]); printf(")\n"); } int main(void) { struct VECTOR x = {3, {100.0, 200.0, 300.0}, "x"}; vector_print(&x); vector_func(&x); // アドレスを渡す. vector_print(&x); // x のメンバーの値が変更されていることに注意. return EXIT_SUCCESS; }
(*v).n と表記する代わりに 矢印(->)を用いてv->nと表記するのが普通の書き方.
program 15
#include <stdio.h> #include <string.h> struct VECTOR { int dimension; double val[10]; char label[100]; }; // VECTOR型ポインタ変数 (VECTOR型の変数/定数のアドレス) を受け取る関数 void vector_func(struct VECTOR *a) { // 関数の中 printf("(2) %s\n", a->label); printf("(2) %d\n", a->dimension); printf("(2) %f\n", a->val[0]); printf("(2) %f\n", a->val[1]); printf("(2) %f\n", a->val[2]); // main 中のxに代入. strcpy(a->label, "vector a"); a->dimension = 3; a->val[0] = 1000.0; a->val[1] = 2000.0; a->val[2] = 3000.0; } int main(void) { struct VECTOR x = {3, {100.0, 200.0, 300.0}, "x"}; // 呼び出し前 printf("(1) %s\n", x.label); printf("(1) %d\n", x.dimension); printf("(1) %f\n", x.val[0]); printf("(1) %f\n", x.val[1]); printf("(1) %f\n", x.val[2]); vector_func(&x); // アドレスを渡す. // 呼び出し後 printf("(3) %s\n", x.label); printf("(3) %d\n", x.dimension); printf("(3) %f\n", x.val[0]); printf("(3) %f\n", x.val[1]); printf("(3) %f\n", x.val[2]); return 0; }
program 16
#include <stdio.h> #include <stdlib.h> #include <string.h> #define NMAX 10 #define LABELMAX 100 struct VECTOR { int n; double val[NMAX]; char label[LABELMAX]; }; void vector_func(struct VECTOR *v) { int i; for ( i = 0; i < v->n; i++ ) v->val[i] += 1000.0; } void vector_print(struct VECTOR *a) { int i; printf("%s = ( ", a->label); for ( i = 0; i < a->n; i++ ) printf("%g ", a->val[i]); printf(")\n"); } int main(void) { struct VECTOR x = {3, {100.0, 200.0, 300.0}, "x"}; vector_print(&x); vector_func(&x); vector_print(&x); return EXIT_SUCCESS; }
練習として, 前述のベクトルの和を計算する関数vector_addを, アドレスを受け取る関数に書き換えると次のようになる.
program 17
#include <stdio.h> #include <stdlib.h> #include <string.h> #define NMAX 10 #define LABELMAX 100 struct VECTOR { int n; double val[NMAX]; char label[LABELMAX]; }; // v = a + b を計算する関数. void vector_add(struct VECTOR *v, struct VECTOR *a, struct VECTOR *b) { int i; sprintf(v->label, "%s + %s", a->label, b->label); if(a->n < b->n) v->n = a->n; else v->n = b->n; for ( i = 0; i < v->n; i++ ) v->val[i] = a->val[i] + b->val[i]; } void vector_print(struct VECTOR *a) { int i; printf("%s = ( ", a->label); for ( i = 0; i < a->n; i++ ) printf("%g ", a->val[i]); printf(")\n"); } int main(void) { struct VECTOR c10 = {3, { 10.0, 10.0, 10.0}, "10"}; struct VECTOR x = {3, {100.0, 200.0, 300.0}, "x" }; struct VECTOR y = {3, {0.0} , "y" }; vector_print(&y); vector_add(&y, &x, &c10); vector_print(&y); return EXIT_SUCCESS; }
program 18
#include <stdio.h> #include <stdlib.h> #include <string.h> #define NMAX 10 #define LABELMAX 100 struct VECTOR { int n; double val[NMAX]; char label[LABELMAX]; }; // 構造体のアドレスを返す関数 struct VECTOR *vector_add(struct VECTOR *v, struct VECTOR *a, struct VECTOR *b) { int i; sprintf(v->label, "%s + %s", a->label, b->label); if(a->n < b->n) v->n = a->n; else v->n = b->n; for ( i = 0; i < v->n; i++ ) v->val[i] = a->val[i] + b->val[i]; return v; } void vector_print(struct VECTOR *a) { int i; printf("%s = ( ", a->label); for ( i = 0; i < a->n; i++ ) printf("%g ", a->val[i]); printf(")\n"); } int main(void) { struct VECTOR c10 = {3, { 10.0, 10.0, 10.0}, "10"}; // 領域確保 struct VECTOR x = {3, {100.0, 200.0, 300.0}, "x" }; // 領域確保 struct VECTOR y = {3, {0.0} , "y" }; // 領域確保 struct VECTOR *z; // ポインター変数 z = &y; vector_print(z); z = vector_add(&y, &x, &c10); vector_print(z); return EXIT_SUCCESS; }
再度先に挙げたアドレスによる参照をを行わないプログラムについて, 領域確保の視点から, コメントを入れておくので両者を比較してみるとよい.
program 19
#include <stdio.h> #include <stdlib.h> #include <string.h> #define NMAX 10 #define LABELMAX 100 struct VECTOR { int n; double val[NMAX]; char label[LABELMAX]; }; // 呼び出し時に // a の領域確保 // a = y の代入が起きる. void vector_print(struct VECTOR a) { int i; printf("%s = ( ", a.label); for(i=0; i<a.n; i++) printf("%g ", a.val[i]); printf(")\n"); } // 呼び出し時に // a の領域確保, b の領域確保 // a = x の代入, b = c10 の代入が起きる. struct VECTOR vector_add(struct VECTOR a, struct VECTOR b) { int i; struct VECTOR tmp; // 領域確保 // sprintf は printf の結果を文字列 tmp.label に書き込む関数. sprintf(tmp.label, "%s + %s", a.label, b.label); if ( a.n < b.n ) tmp.n = a.n; else tmp.n = b.n; for(i=0; i< a.n; i++) tmp.val[i] = a.val[i] + b.val[i]; return tmp; // アドレスではなく 複数のメンバーの値が含まれる tmp そのものを返す. } int main(void) { struct VECTOR c10 = {3, { 10.0, 10.0, 10.0}, "10"}; struct VECTOR x = {3, {100.0, 200.0, 300.0}, "x" }; struct VECTOR y = {0, {0.0}, "y" }; y = vector_add(x, c10); // y = tmp の代入が起きる. vector_print(y); return EXIT_SUCCESS; }
program 20
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #define NMAX 10 #define LABELMAX 100 struct VECTOR { int n; double val[NMAX]; char label[LABELMAX]; }; // 構造体へのポインタ(構造体のアドレス)を返す関数 struct VECTOR *vector_add(struct VECTOR *v, struct VECTOR *a, struct VECTOR *b) { int i; sprintf(v->label, "(%s + %s)", a->label, b->label); if(a->n < b->n) v->n = a->n; else v->n = b->n; for ( i = 0; i < v->n; i++ ) v->val[i] = a->val[i] + b->val[i]; return v; // ここで結果を代入した構造体のアドレスを返しておくと便利. } // ベクトルa, bで表される2点間の距離を求める関数. double vector_length(struct VECTOR *a, struct VECTOR *b) { int i; double sum = 0.0; if ( a->n != b->n ) { printf("type mismatch\n"); exit(0); } for(i=0; i < a->n; i++) sum += (a->val[i] - b->val[i]) * (a->val[i] - b->val[i]); return sqrt(sum); } void vector_print(struct VECTOR *a) { int i; printf("%s = ( ", a->label); for ( i = 0; i < a->n; i++ ) printf("%g ", a->val[i]); printf(")\n"); } int main(void) { struct VECTOR c10 = {3, { 10.0, 10.0, 10.0}, "10"}; struct VECTOR x = {3, {100.0, 200.0, 300.0}, "x" }; //結果を代入するための領域は呼出側で用意する. struct VECTOR y = {3, {0.0} , "y" }; vector_print(&y); vector_add(&y, &x, &c10); // 呼出側で結果を代入する領域yを用意して渡す. vector_print(&y); // vector_add が結果を代入した構造体のアドレスを返すようにしてある. // このようにしておくと, その結果を更に別の関数に渡して使える. vector_print(vector_add(&y, &x, &c10)); return EXIT_SUCCESS; }
プログラムを改変していくに従って, 関数に渡す引数の数が変わっていく場合や, 関数から複数の値を返したい場合で, あまり大きくない構造体の場合は, 値を渡したり返したりしてもいいかもしれない.
アドレスでなく値を用いるこの書き方の方は, double型変数などと同じ表記をすればよいのでわかりやすい. ただし, 途中経過を記憶する領域を必要とする点, 常に全ての要素の代入が起き, 時間がかかる点で劣る.
program 21
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #define NMAX 10 #define LABELMAX 100 struct VECTOR { int n; double val[NMAX]; char label[LABELMAX]; }; // 構造体(の値)を受け取り, 構造体(の値)を返す関数 // 関数内で宣言された構造体の変数v は, 通常の変数と同じ 寿命を持ち, // 返り値としての扱いも同じ. struct VECTOR vector_add(struct VECTOR a, struct VECTOR b) { struct VECTOR v; //関数内で宣言された変数の寿命は関数が終了するまで. int i; sprintf(v.label, "(%s + %s)", a.label, b.label); if(a.n < b.n) v.n = a.n; else v.n = b.n; for ( i = 0; i < v.n; i++ ) v.val[i] = a.val[i] + b.val[i]; return v; // 構造体の値を返すことができる. } // ベクトルa, bで表される2点間の距離を求める関数. double vector_length(struct VECTOR a, struct VECTOR b) { int i; double sum = 0.0; double length;//関数内で宣言された変数の寿命は関数が終了するまで. if ( a.n != b.n ) { printf("type mismatch\n"); exit(0); } for(i=0; i < a.n; i++) sum += (a.val[i] - b.val[i]) * (a.val[i] - b.val[i]); length = sum; return length; // double 型変数の値を返すことができる. } void vector_print(struct VECTOR a) { int i; printf("%s = ( ", a.label); for ( i = 0; i < a.n; i++ ) printf("%g ", a.val[i]); printf(")\n"); } int main(void) { struct VECTOR c10 = {3, { 10.0, 10.0, 10.0}, "10"}; struct VECTOR x = {3, {100.0, 200.0, 300.0}, "x" }; //結果を代入するための領域は呼出側で用意する. struct VECTOR y = {3, {0.0} , "y" }; vector_print(y); y = vector_add(x, c10); // 呼出側で結果を代入する領域yを用意して代入する. vector_print(y); // vector_add が結果を代入した構造体(の値)を返すようにしてある. // このようにしておくと, その結果を更に別の関数に渡して使える. vector_print(vector_add(x, c10)); return EXIT_SUCCESS; }
SVGは Webなどで用いることができる Scalable Vector Grapohics である. jpg, bmp などの画像と異なり, どんなに拡大してもギザギザにならない.
SVGファイルは, 例えば下のような内容を持っている. この内容をを emacs, gedit, メモ帳などのエディタに写し, aaa.svg のように 拡張子として.svgがつく ファイル名で保存してしてみよう.
<?xml version='1.0' encoding='UTF-8' standalone='no'?> <svg xmlns:svg='http://www.w3.org/2000/svg' xmlns='http://www.w3.org/2000/svg' version='1.0' width='700' height='1000'> <g> <path d='M 100.0,200.0 L 500.0,500.0' style='stroke:#ff0000;stroke-width:10px' /> </g> </svg>
下に示すように, このファイルは Firefox などのWebブラウザで「ファイルを開い」たり, Webブラウザにドラッグしたりすることで, 表示できる.
再度, エディタなどでファイルを変更し保存した後は, ブラウザの再読み込みで新しい内容を表示し直すことができるブラウザもある.
SVGファイルの構造は次のようになっている.
stroke 線の色を指定す。16進数で rrggbbの形式. ここで rrがRedの明るさ, ggがGreenの明るさ, bbがBlueの明るさ. 各色について 00 から ff までの256階調を指定できる. 例えば #1289cd 赤 0x12, 緑 0x89, 青 0xcdを混ぜた色.
stroke-width は線の太さ.
上のSVGファイルを出力するするプログラムは以下のようになる.
#include <stdio.h> #include <stdlib.h> int main(void) { printf("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"); printf("<svg\n"); printf(" xmlns:svg='http://www.w3.org/2000/svg'\n"); printf(" xmlns='http://www.w3.org/2000/svg'\n"); printf(" version='1.0'\n"); printf(" width='700'\n"); printf(" height='1000'>\n"); printf(" <g>\n"); printf(" <path\n"); printf(" d='M 100.0,200.0 L 500.0,500.0'\n"); printf(" style='stroke:#ff0000;stroke-width:10px' />\n"); printf(" </g>\n"); printf("</svg>\n"); return EXIT_SUCCESS; }
この結果をファイルに保存するためには, リダイレクトを用いる. そのためには, 後述するように端末の中でプログラムを実行する.
$ ./a.out > bbb.svg
$ more bbb.svg
$ ls
$ dir
$ Z:
$ cd folderA一つ上(親)のディレクトリに移動する場合は, 親のディレクトリを表す..という特殊な名前を用いる.
$ cd ..
$ abc.exeUNIX の場合
$ ./a.out
出力結果をファイルに保存する場合は,
Windowsの場合
$ abc.exe > xyz.svgUNIX の場合
$ ./a.out > xyz.svg
出力先のファイルが既に存在する場合は, 上書きされてしまうので十分注意すること.
変化させたい部分を変数にすると以下のようになる.
#include <stdio.h> #include <stdlib.h> int main(void) { double width = 500.0; // 画面の幅 double height = 1000.0; // 画面の高さ double x0 = 100.0, y0 = 200.0; // 直線の始点の座標 double x1 = 500.0, y1 = 500.0; // 直線の終点の座標 unsigned stroke = 0x0fff00; // 直線の色 unsigned stroke_width = 10; // 直線の太さ printf("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"); printf("<svg\n"); printf(" xmlns:svg='http://www.w3.org/2000/svg'\n"); printf(" xmlns='http://www.w3.org/2000/svg'\n"); printf(" version='1.0'\n"); printf(" width='%g'\n", width); printf(" height='%g'>\n", height); printf(" <g>\n"); printf(" <path\n"); printf(" d='M %g,%g L %g,%g'\n", x0, y0, x1, y1); printf(" style='stroke:#%06x;stroke-width:%upx' />\n", stroke, stroke_width); printf(" </g>\n"); printf("</svg>\n"); return EXIT_SUCCESS; }
関数化すると見通しがよくなる. 一度開始と終了のための関数を作っておけば, 以降はmain関数の中でこれらを呼ぶだけで済む.
#include <stdio.h> #include <stdlib.h> void svg_begin(double w, double h) { printf("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"); printf("<svg\n"); printf(" xmlns:svg='http://www.w3.org/2000/svg'\n"); printf(" xmlns='http://www.w3.org/2000/svg'\n"); printf(" version='1.0'\n"); printf(" width='%g'\n", w); printf(" height='%g'>\n", h); printf(" <g>\n"); } void line_print(double p0, double q0, double p1, double q1, unsigned s, unsigned w) { printf(" <path\n"); printf(" d='M %g,%g L %g,%g'\n", p0, q0, p1, q1); printf(" style='stroke:#%06x;stroke-width:%upx' />\n", s, w); } void svg_end(void) { printf(" </g>\n"); // group 終了 printf("</svg>\n"); // svg 終了 } int main(void) { double width = 500.0; // 画面の幅 double height = 1000.0; // 画面の高さ double x0 = 100.0, y0 = 500.0; // 直線の始点の座標 double x1 = 500.0, y1 = 200.0; // 直線の終点の座標 unsigned stroke = 0x0fff00; // 直線の色 unsigned stroke_width = 25; // 直線の太さ svg_begin(width, height); // SVG 開始 line_print(x0, y0, x1, y1, stroke, stroke_width); // 色々な描画をここに記述. svg_end(); // SVG 終了 return EXIT_SUCCESS; }
直線を描画する関数にいろいろな値を入れて呼び出すと, その数だけ直線が描ける. main の部分だけを変えれば自由に描画ができる. line_print関数を呼び出す実引数の部分を変更し, 色々な直線を描いてみましょう.
#include <stdio.h> #include <stdlib.h> void svg_begin(double w, double h) { printf("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"); printf("<svg\n"); printf(" xmlns:svg='http://www.w3.org/2000/svg'\n"); printf(" xmlns='http://www.w3.org/2000/svg'\n"); printf(" version='1.0'\n"); printf(" width='%g'\n", w); printf(" height='%g'>\n", h); printf(" <g>\n"); } void line_print(double p0, double q0, double p1, double q1, unsigned s, double w) { printf(" <path\n"); printf(" d=\"M %g,%g L %g,%g\"\n", p0, q0, p1, q1); printf(" style='stroke:#%06x;stroke-width:%gpx' />\n", s, w); } void svg_end(void) { printf(" </g>\n"); // group 終了 printf("</svg>\n"); // svg 終了 } int main(void) { svg_begin(500, 500); // SVG 開始 line_print(100.0, 500.0, 50.0, 300, 0xff0000, 2.0); line_print(200.0, 400.0, 60, 400, 0x00ff00, 5.0); line_print(300.0, 300.0, 70.0, 500, 0x0000ff, 10.0); svg_end(); // SVG 終了 return EXIT_SUCCESS; }
#include <stdio.h> #include <stdlib.h> struct SVG { double width; // 画面の幅 double height; // 画面の高さ }; struct LINE { double x0; // 始点のx座標 double y0; // 始点のy座標 double x1; // 終点のx座標 double y1; // 終点のy座標 unsigned int stroke; // 線の色 0xrrggbb double stroke_width; }; void svg_begin(struct SVG s) { printf("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"); printf("<svg\n"); printf(" xmlns:svg='http://www.w3.org/2000/svg'\n"); printf(" xmlns='http://www.w3.org/2000/svg'\n"); printf(" version='1.0'\n"); printf(" width='%g'\n", s.width); printf(" height='%g'>\n", s.height); printf(" <g>\n"); } void line_print(struct LINE p) { printf(" <path\n"); printf(" d='M %g,%g L %g,%g'\n", p.x0, p.y0, p.x1, p.y1); printf(" style='stroke:#%06x;stroke-width:%gpx' />\n", p.stroke, p.stroke_width); } void svg_end(void) { printf(" </g>\n"); // group 終了 printf("</svg>\n"); // svg 終了 } int main(void) { struct SVG svg; struct LINE line; svg.width = 500.0; // 構造体メンバへの代入. svg.height = 1000.0; line.x0 = 10.0; line.y0 = 100.0; line.x1 = 300.0; line.y1 = 700.0; line.stroke = 0xff00ff; line.stroke_width = 4.0; svg_begin(svg); // 構造体を関数に渡す. line_print(line); svg_end(); // SVG 終了 return EXIT_SUCCESS; }
構造体の初期化. 上のプログラムと比べmain関数の部分のみを変更.
#include <stdio.h> #include <stdlib.h> struct SVG { double width; // 画面の幅 double height; // 画面の高さ }; struct LINE { double x0; // 始点のx座標 double y0; // 始点のy座標 double x1; // 終点のx座標 double y1; // 終点のy座標 unsigned int stroke; // 線の色 0xrrggbb double stroke_width; }; void svg_begin(struct SVG s) { printf("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"); printf("<svg\n"); printf(" xmlns:svg='http://www.w3.org/2000/svg'\n"); printf(" xmlns='http://www.w3.org/2000/svg'\n"); printf(" version='1.0'\n"); printf(" width='%g'\n", s.width); printf(" height='%g'>\n", s.height); printf(" <g>\n"); } void line_print(struct LINE p) { printf(" <path\n"); printf(" d='M %g,%g L %g,%g'\n", p.x0, p.y0, p.x1, p.y1); printf(" style='stroke:#%06x;stroke-width:%gpx' />\n", p.stroke, p.stroke_width); } void svg_end(void) { printf(" </g>\n"); // group 終了 printf("</svg>\n"); // svg 終了 } int main(void) { // 構造体の初期化. 定義した順に並べる. struct SVG svg = {500.0, 1000.0}; struct LINE line = {10.0, 100.0, 300.0, 700.0, 0xff0000, 4.0}; svg_begin(svg); // 構造体を関数に渡す. line_print(line); svg_end(); // SVG 終了 return EXIT_SUCCESS; }
構造体の代入. 同様に main 関数の部分のみを変更. 構造体の代入では, 代入元の各メンバーの値が, 代入先の各メンバーに代入される.
#include <stdio.h> #include <stdlib.h> struct SVG { double width; // 画面の幅 double height; // 画面の高さ }; struct LINE { double x0; // 始点のx座標 double y0; // 始点のy座標 double x1; // 終点のx座標 double y1; // 終点のy座標 unsigned int stroke; // 線の色 0xrrggbb double stroke_width; }; void svg_begin(struct SVG s) { printf("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"); printf("<svg\n"); printf(" xmlns:svg='http://www.w3.org/2000/svg'\n"); printf(" xmlns='http://www.w3.org/2000/svg'\n"); printf(" version='1.0'\n"); printf(" width='%g'\n", s.width); printf(" height='%g'>\n", s.height); printf(" <g>\n"); } void line_print(struct LINE p) { printf(" <path\n"); printf(" d='M %g,%g L %g,%g'\n", p.x0, p.y0, p.x1, p.y1); printf(" style='stroke:#%06x;stroke-width:%gpx' />\n", p.stroke, p.stroke_width); } void svg_end(void) { printf(" </g>\n"); // group 終了 printf("</svg>\n"); // svg 終了 } int main(void) { struct SVG svg = {500.0, 1000.0}; struct LINE line = {10.0, 100.0, 300.0, 700.0, 0xff00ff, 4.0}; struct LINE line2, line3; line2 = line; // line の各メンバーの値を line2 の各メンバーに代入. line3 = line; // line の各メンバーの値を line2 の各メンバーに代入. line2.x0 = 100.0; // line2.x0 のみ変更 line3.x0 = 200.0; // line3.x0 のみ変更 svg_begin(svg); line_print(line); line_print(line2); line_print(line3); svg_end(); return EXIT_SUCCESS; }
関数の戻り値とすることができる. (配列では各要素を返して代入することはできない.) 新しく struct LINE line_default(void) 関数を作成した. この関数の構造体としての戻り値を, 別の構造体 line2 および line3 に代入している.
#include <stdio.h> #include <stdlib.h> struct SVG { double width; // 画面の幅 double height; // 画面の高さ }; struct LINE { double x0; // 始点のx座標 double y0; // 始点のy座標 double x1; // 終点のx座標 double y1; // 終点のy座標 unsigned int stroke; // 線の色 0xrrggbb double stroke_width; }; void svg_begin(struct SVG s) { printf("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"); printf("<svg\n"); printf(" xmlns:svg='http://www.w3.org/2000/svg'\n"); printf(" xmlns='http://www.w3.org/2000/svg'\n"); printf(" version='1.0'\n"); printf(" width='%g'\n", s.width); printf(" height='%g'>\n", s.height); printf(" <g>\n"); } struct LINE line_default(void) { struct LINE line = {10.0, 100.0, 300.0, 700.0, 0xff00ff, 4.0}; return line; } void line_print(struct LINE p) { printf(" <path\n"); printf(" d='M %g,%g L %g,%g'\n", p.x0, p.y0, p.x1, p.y1); printf(" style='stroke:#%06x;stroke-width:%gpx' />\n", p.stroke, p.stroke_width); } void svg_end(void) { printf(" </g>\n"); // group 終了 printf("</svg>\n"); // svg 終了 } int main(void) { struct SVG svg = {500.0, 1000.0}; struct LINE line2, line3; // line_default 関数は struct LINE を返す. // その各メンバーの値を line2, line3 の各メンバーに代入. line2 = line_default(); line3 = line_default(); line2.x0 = 100.0; // line2.x0 のみ変更 line3.x0 = 200.0; // line3.x0 のみ変更 svg_begin(svg); line_print(line2); line_print(line3); svg_end(); return EXIT_SUCCESS; }
構造体の配列. 配列要素の後ろにメンバーの指定をする.
#include <stdio.h> #include <stdlib.h> struct SVG { double width; // 画面の幅 double height; // 画面の高さ }; struct LINE { double x0; // 始点のx座標 double y0; // 始点のy座標 double x1; // 終点のx座標 double y1; // 終点のy座標 unsigned int stroke; // 線の色 0xrrggbb double stroke_width; }; void svg_begin(struct SVG s) { printf("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"); printf("<svg\n"); printf(" xmlns:svg='http://www.w3.org/2000/svg'\n"); printf(" xmlns='http://www.w3.org/2000/svg'\n"); printf(" version='1.0'\n"); printf(" width='%g'\n", s.width); printf(" height='%g'>\n", s.height); printf(" <g>\n"); } void line_print(struct LINE p) { printf(" <path\n"); printf(" d='M %g,%g L %g,%g'\n", p.x0, p.y0, p.x1, p.y1); printf(" style='stroke:#%06x;stroke-width:%gpx' />\n", p.stroke, p.stroke_width); } void svg_end(void) { printf(" </g>\n"); // group 終了 printf("</svg>\n"); // svg 終了 } #define NLINES 10 int main(void) { int i; struct SVG svg = {500.0, 1000.0}; struct LINE line0 = {10.0, 100.0, 300.0, 700.0, 0xff00ff, 4.0}; struct LINE lines[NLINES]; for(i=0; i<NLINES; i++) lines[i] = line0; // lines の各要素を line0 で初期化. for(i=0; i<NLINES; i++) lines[i].x0 = i*20.0; // lines の各要素の x0 を変更. for(i=0; i<NLINES; i++) lines[i].stroke =0x808000 + i*0x1010; // lines の各要素の stroke を変更. svg_begin(svg); for(i=0; i<NLINES; i++) line_print(lines[i]); svg_end(); return EXIT_SUCCESS; }
構造体へのポインタを関数に渡す. 以下の点に注意.
関数に渡す場合は次のようになる. 構造体を受け取る関数を全て書き換えた.
#include <stdio.h> #include <stdlib.h> struct SVG { double width; // 画面の幅 double height; // 画面の高さ }; struct LINE { double x0; // 始点のx座標 double y0; // 始点のy座標 double x1; // 終点のx座標 double y1; // 終点のy座標 unsigned int stroke; // 線の色 0xrrggbb double stroke_width; }; void svg_begin(struct SVG *s) { printf("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"); printf("<svg\n"); printf(" xmlns:svg='http://www.w3.org/2000/svg'\n"); printf(" xmlns='http://www.w3.org/2000/svg'\n"); printf(" version='1.0'\n"); printf(" width='%g'\n", s->width); printf(" height='%g'>\n", s->height); printf(" <g>\n"); } void line_print(struct LINE *p) { printf(" <path\n"); printf(" d='M %g,%g L %g,%g'\n", p->x0, p->y0, p->x1, p->y1); printf(" style='stroke:#%06x;stroke-width:%gpx' />\n", p->stroke, p->stroke_width); } void svg_end(void) { printf(" </g>\n"); // group 終了 printf("</svg>\n"); // svg 終了 } int main(void) { struct SVG svg = {500.0, 1000.0}; struct LINE line = {10.0, 100.0, 300.0, 700.0, 0xff00ff, 4.0}; struct LINE line2, line3; line2 = line; // line の各メンバーの値を line2 の各メンバーに代入. line3 = line; // line の各メンバーの値を line2 の各メンバーに代入. line2.x0 = 100.0; // line2.x0 のみ変更 line3.x0 = 200.0; // line3.x0 のみ変更 svg_begin(&svg); line_print(&line); line_print(&line2); line_print(&line3); svg_end(); return EXIT_SUCCESS; }
main関数の中で, line3 を line を指すポインタとすることにした. line3が指すアドレスの内容を変更すると, line1 のメンバが変更されることに注意. また, 関数に渡すとき, &line1 のようにline1にはアドレス演算子が必要であるし, line3には, line3のように付けないことに注意すること.
#include <stdio.h> #include <stdlib.h> struct SVG { double width; // 画面の幅 double height; // 画面の高さ }; struct LINE { double x0; // 始点のx座標 double y0; // 始点のy座標 double x1; // 終点のx座標 double y1; // 終点のy座標 unsigned int stroke; // 線の色 0xrrggbb double stroke_width; }; void svg_begin(struct SVG *s) { printf("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"); printf("<svg\n"); printf(" xmlns:svg=\"http://www.w3.org/2000/svg\"\n"); printf(" xmlns=\"http://www.w3.org/2000/svg\"\n"); printf(" version=\"1.0\"\n"); printf(" width=\"%g\"\n", s->width); printf(" height=\"%g\">\n", s->height); printf(" <g>\n"); } void line_print(struct LINE *p) { printf(" <path\n"); printf(" d=\"M %g,%g L %g,%g\"\n", p->x0, p->y0, p->x1, p->y1); printf(" style=\"stroke:#%06x;stroke-width:%gpx\" />\n", p->stroke, p->stroke_width); } void svg_end(void) { printf(" </g>\n"); // group 終了 printf("</svg>\n"); // svg 終了 } int main(void) { struct SVG svg = {500.0, 1000.0}; struct LINE line = {10.0, 100.0, 300.0, 700.0, 0xff00ff, 4.0}; struct LINE line2, *line3; line2 = line; // line の各メンバーの値を line2 の各メンバーに代入. line3 = &line; // line3 は lineを指す. line2.x0 = 100.0; // line2.x0 のみ変更 line3->x0 = 200.0; // line1.x0 が変更される. svg_begin(&svg); line_print(&line); // アドレスを渡す. &lineがアドレス. line_print(&line2); line_print(line3); // アドレスを渡す. line3 がアドレス. svg_end(); return EXIT_SUCCESS; }
配列名はアドレスを保持することに注意すると, 仮引数がポインタ変数のときには, 構造体の配列を渡すときは以下のようになる. あらたに void lines_print(struct LINE *p, int n)関数を作成した.
#include <stdio.h> #include <stdlib.h> struct SVG { double width; // 画面の幅 double height; // 画面の高さ }; struct LINE { double x0; // 始点のx座標 double y0; // 始点のy座標 double x1; // 終点のx座標 double y1; // 終点のy座標 unsigned int stroke; // 線の色 0xrrggbb double stroke_width; }; void svg_begin(struct SVG *s) { printf("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"); printf("<svg\n"); printf(" xmlns:svg=\"http://www.w3.org/2000/svg\"\n"); printf(" xmlns=\"http://www.w3.org/2000/svg\"\n"); printf(" version=\"1.0\"\n"); printf(" width=\"%g\"\n", s->width); printf(" height=\"%g\">\n", s->height); printf(" <g>\n"); } struct LINE line_default(void) { struct LINE line = {10.0, 100.0, 300.0, 700.0, 0xff00ff, 4.0}; return line; } void line_print(struct LINE *p) { printf(" <path\n"); printf(" d=\"M %g,%g L %g,%g\"\n", p->x0, p->y0, p->x1, p->y1); printf(" style=\"stroke:#%06x;stroke-width:%gpx\" />\n", p->stroke, p->stroke_width); } void lines_print(struct LINE *p, int n) { int i; for(i=0; i<n; i++){ printf(" <path\n"); printf(" d=\"M %g,%g L %g,%g\"\n", p[i].x0, p[i].y0, p[i].x1, p[i].y1); printf(" style=\"stroke:#%06x;stroke-width:%gpx\" />\n", p[i].stroke, p[i].stroke_width); } } void svg_end(void) { printf(" </g>\n"); // group 終了 printf("</svg>\n"); // svg 終了 } #define NLINES 10 int main(void) { int i; struct SVG svg = {500.0, 1000.0}; struct LINE lines[NLINES]; for(i=0; i<NLINES; i++) lines[i] = line_default(); for(i=0; i<NLINES; i++) lines[i].x0 = i*20.0; // 各要素の x0 を変更. for(i=0; i<NLINES; i++) lines[i].stroke =0x808000 + i*0x1010; // 各要素の stroke を変更. svg_begin(&svg); lines_print(lines, NLINES); svg_end(); return EXIT_SUCCESS; }
(c)1999-2013 Tetsuya Makimura