C プログラミング入門 総機1 (月1) 13: 構造体 Linux にログインし、以下の講義ページ を開いておくこと http://www-it.sci.waseda.ac.jp/ teachers/w483692/CPR1/ 2015-07-06 1 例題:多角形の面積 n = 5 (5角形) の例 𝑛−1 𝑆= 𝒑1 𝒑0 𝑇0 𝜃 𝒑2 𝑆 𝟎 𝑛−1 𝑇𝑖 = 𝑖=0 𝑖=0 1 𝒑𝑖 2 𝒑𝑖+1 where 𝒑𝑛 = 𝒑0 二次元の位置ベクトル 原点 (ゼロベクトル) 𝒑4 𝒑3 2015-07-06 この計算は、𝑇𝑖 を符号付き面積とし、頂点の順に 番号付けを行うことで、原点の位置によらない。 C プログラミング入門 総機1 (月1) 2 例題:多角形の面積 // n 個の x 座標列 px と, y 座標列 py の作る多角形の面積 // ただし、点は左回りに並んでいるとする double PolygonArea(double *px, double *py, int n) { double S = 0.0; int i; double px[] と書いてもよい for(i = 0; i < n; ++i) { S += TriangleArea(px[i], py[i], px[(i+1)%n], py[(i+1)%n]); } return S; i == n-1 の時、 0 となる } 1 つ目の点の座標 *px 1.0 −2.4 −5.0 −1.5 2.0 *py 1.0 1.3 −0.2 −2.0 −0.3 2015-07-06 C プログラミング入門 総機1 (月1) 3 例題:多角形の面積 // 3点 (0,0), (x1, y1), (x2, y2) のなす三角形の面積 // 時計回りに並んでいる場合正、そうでない場合は負となる double TriangleArea(double x1, double y1, double x2, double y2) { return (x1*y2 - x2*y1) / 2.0; } 𝒑2 = 𝑥2 , 𝑦2 ⊤ 𝒑1 = 𝑥1 , 𝑦1 𝑇 ⊤ 𝜃 𝟎 1 1 1 𝑇 = 𝒑1 𝒑2 sin 𝜃 = 𝒑1 𝒑2 = 𝑥1 𝑦2 − 𝑥2 𝑦1 2 2 2 C プログラミング入門 総機1 (月1) 2015-07-06 4 例題:多角形の面積 観察ポイント 点の座標 x, y がバラバラに扱われている 関数の引数の数が多い 点を表す新しい型を作れないか? たとえば… バラバラに扱う場合 double PolygonArea(double *px, double *py, int n); 引数の総数が 減っている POINT という型で表した場合 double PolygonArea(POINT *p, int n); double TriangleArea(double x1, double TriangleArea(POINT p1, double y1, double x2, double y2); POINT p2); 2015-07-06 C プログラミング入門 総機1 (月1) 5 構造体の概要 複数の変数をまとめて一つの変数として扱う 方法 int や double といった型と同じように使用 可能 構造体 Point 型の定義 構造体 Point 型を使ったコード typedef struct { double x, y; } Point; { double S; Point P1 = { 1, 2 }; Point P2 = { -2, 1 }; S = TriangleArea(P1, P2); ... 今日の目標: このようなコードを書けるようになること 2015-07-06 C プログラミング入門 総機1 (月1) 6 構造体の説明目次 構造体の定義 構造体型の変数と typedef 構造体の初期化 構造体の読み書き 構造体のコピー 関数による構造体の扱い 構造体へのポインタ 2015-07-06 C プログラミング入門 総機1 (月1) 7 構造体の定義 キーワード struct TagName { int x, y; double a, b, c; }; 構造体を区別するためのタグ名 任意個数のメンバ変数 セミコロンで終わる 構造体は一つのメモリ領域に複数の変数を格 納する それぞれをメンバ (member) という 2015-07-06 C プログラミング入門 総機1 (月1) 8 構造体型の変数 メモリ上のレイアウ ト(順番や隙間の大 きさ)は環境による ... struct Point { double x, y; }; struct Point p1 double x int main(void) { struct Point p1, p2; ... double y struct Point p2 double x double y 「struct + タグ名」で 一つの型名を構成する 型名として "struct TagName" を使う 2015-07-06 C プログラミング入門 総機1 (月1) 9 構造体型の変数 ... typedef struct Point { double x, y; } Point; 構造体型を Point とい う型名として定義する struct Point p1 double x double y この場合、タグ名を省略してもよい タグ名と型名は同じで構わない int main(void) { struct Point p1; Point p2; ... 1 単語で型名を書ける タグ名を _Point struct Point や p2Point_tag の様 に区別する流儀もある double x double y また、構造体の別名をすべて大文字 で表す流儀もある struct キーワードを付けるのは面倒なので、 多くの場合 typedef を使って別名を定義 2015-07-06 C プログラミング入門 総機1 (月1) 10 構造体の初期化 ... typedef struct { double x, y; } Point; メンバのかかれて いる順に与える 初期化が一部のみ指 定されている場合は、 残りはすべてゼロと なる int main(void) { Point p1 = { 1.0, 2.0 }; Point p2; ... struct Point p1 double x double y 1.0 2.0 struct Point p2 double x ? double y ? 構造体の初期化は、配列と似た記法を用いる 初期化をしない自動変数の値は不定 2015-07-06 C プログラミング入門 総機1 (月1) 11 通常の変数と同様 構造体の初期化 ... typedef struct { double x, y; } Point; struct Point p1 double x double y 1.0 int main(void) { Point p1 = { 1.0, 2.0 }; Point p2 = p1; すべてのメンバが ... コピーされる 2.0 struct Point p2 double x 1.0 double y 2.0 同じ構造体の別の変数を与えることにより、 すべてのメンバのコピーで初期化する 2015-07-06 配列との大きな違い C プログラミング入門 総機1 (月1) 12 構造体のアクセス 構造体のメンバは . 演算子を使う ... typedef struct { double x, y; } Point; struct Point p1 double x 1.0 double y 2.0 メンバ変数を読む int main(void) メンバ変数に代入する { Point p1 = { 1.0, 2.0 }; printf("p1=(%f,%f)\n", p1.x, p1.y); p1.x = -p1.y; printf("p1=(%f,%f)\n", p1.x, p1.y); ... 2015-07-06 出力 p1=(1.000000,2.000000) p1=(-2.000000,2.000000) C プログラミング入門 総機1 (月1) 13 構造体のコピー 構造体は代入演算子によりコピーできる ... typedef struct { double x, y; } Point; int main(void) { Point p1 = { 1.0, 2.0 }; Point p2 = p1; 初期化のイコール Point p3; p3 = p1; ... 代入演算子によるコピー 2015-07-06 C プログラミング入門 総機1 (月1) 14 関数で構造体を扱う 構造体は関数の引数、戻り値として使える ... typedef struct { double x, y; } Point; 実引数がコピーが 渡される // 中点を計算し戻り値として返す Point midpoint(Point p1, Point p2) { double mx = (p1.x + p2.x)/2.0; double my = (p1.y + p2.y)/2.0; Point m = { mx, my }; return m; } 2015-07-06 配列と異なる int main(void) { Point st = { 1.0, 0.0 }; Point ed = { 2.0, 2.0 }; Point md; md = midpoint(st, ed); ... 仮引数へコピーされる 戻り値がコピーされる C プログラミング入門 総機1 (月1) 15 構造体の配列メンバ 構造体のメンバに配列を持たせることで、配 列のコピーを行うこともできる ... Person X typedef struct char name[20] { "Taro" char name[20]; int age; } Person; 配列を含む構造体自体 配列のコピー のコピーは可能 int main(void) Person Y { Person X = { "Taro", 22 }; char name[20] Person Y; "Taro" Y = X; Y.name = X.name; 配列の直接のコピーは できない C プログラミング入門 総機1 (月1) 2015-07-06 int age 22 int age 22 16 構造体のポインタ変数メンバ ポインタ変数は単純にアドレス値のコピー ポインタの先はコピーしないので浅いコピー (shallow copy) と呼ばれる Person X 配列ではなくポインタ typedef struct { char *name; int age; } Person; 中のポインタはアドレ スがコピーされる char *name int age 22 アドレスのコピー Person Y int main(void) char *name int age { 22 Person X = { "Taro", 22 }; Person Y; システムのメモリ領域 Y = X; "Taro" この代入と同等 Y.name = X.name; 2015-07-06 C プログラミング入門 総機1 (月1) 17 変数の比較 変数の種類 宣言 初期化 代入演算子に よるコピー 基本型 int x; = 25; 可能 配列 文字配列 int a[10]; = { 1, 2, 3 }; 不可能 char s[256]; = "string literal"; 構造体 struct Point P; Point P; = { 20, 30 }; = Q; (ほかの構造体変数) 可能 (内容全体) ポインタ変数 int *p; = &var; char *pstr; = "string literal"; 可能 (アドレス値) typedef struct Point {...} Point; を定義した場合 アドレス演算子や malloc() の戻り値からアドレスを得る 2015-07-06 定義の出現順に対応させる 構造体のサイズが大きい場合はコピー に時間がかかることに注意 C プログラミング入門 総機1 (月1) 18 構造体へのポインタ 以下の場合に使われる 構造体を直接変更する必要がある場合 構造体のコピーに時間がかかるのを避けたい場合 ... // 点の座標を原点に変更する void setOrigin(Point *p) { (*p).x = 0; (*p).y = 0; } 2015-07-06 ポインタ p の内容を読むために、まず デリファレンス演算子 * が必要 p の指す構造体のメンバにアクセスす るために . 演算子を使うのだが、演算 子の優先順位の関係で括弧が必要とな る。 C プログラミング入門 総機1 (月1) 19 構造体へのポインタ (アロー演算子) (*p).x という記述を簡略化するためにアロー 演算子 p->x が用意されている ポインタ p が指す構造体のメンバを直接矢印で指 しているイメージ ... // 点の座標を原点に変更する void setOrigin(Point *p) { p->x = 0; p->y = 0; } 2015-07-06 (*p).x と同じ意味 C プログラミング入門 総機1 (月1) 20 構造体の配列・動的メモリ 通常の配列と同じ 動的メモリで確保する場合は sizeof を使う 10 要素の配列変数の場合 ... { int i; Point points[10]; for(i = 0; i < 10; ++i) { points[i].x = i; points[i].y = i*i; ... 2015-07-06 10 要素の動的メモリの場合 ... { int i; Point *points=malloc(sizeof(Point)*10); for(i = 0; i < 10; ++i) { points[i].x = i; points[i].y = i*i; ... 添え字演算子の優先順位のほうが高い C プログラミング入門 総機1 (月1) 21 構造体の利点・欠点 関連する情報を一つにまとめることができる 他の変数と同じように扱える 関数などでのやり取りが簡潔になる ポインタを介した操作が(さらに)理解しに くい 普通の変数なら . 演算子 ポインタ変数なら -> 演算子 2015-07-06 C プログラミング入門 総機1 (月1) 22 構造体の例 <stdio.h> の FILE OpenCV (一般のライブラリの一つ) の CvPoint, CvSize, CvRect など 秋期「C プログラミング」で扱うリンクリス トや木のようなデータ構造 C++ では変数だけでなく関数も持てるよう に拡張されている (クラスと呼ばれる) 2015-07-06 C プログラミング入門 総機1 (月1) 23 構造体の細かい話 (1) 構造体の変数を以下の構文で直接定義するこ とができるが、あまり使用されない。 struct TagName { ... } varname; 今までの例では最後の変数名を省略し、型の定義 のみを行っている 構造体にビット単位でアクセスすることを可 能にするビットフィールドという機能がある 講義では省略する メモリ効率やバイナリレベルでの互換性のために 使われるが、通常は使用しない 2015-07-06 C プログラミング入門 総機1 (月1) 24 構造体の細かい話 (2) C99 での新機能 メンバ名を指定した初期化 (Designated Initializer) • 例: Point P = { .y = 10, .x = 20 }; 最後のメンバとしてサイズを指定しない配列が書 ける (0長配列) 構造体をリテラルとして書く複合リテラル (compound literal) 2015-07-06 C プログラミング入門 総機1 (月1) 25
© Copyright 2025 ExpyDoc