Transcript 構造体

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