• 検索結果がありません。

02: 変数と標準入出力

N/A
N/A
Protected

Academic year: 2021

シェア "02: 変数と標準入出力"

Copied!
25
0
0

読み込み中.... (全文を見る)

全文

(1)

13: 構造体

Linux にログインし、以下の講義ページ

を開いておくこと

http://www-it.sci.waseda.ac.jp/

teachers/w483692/CPR1/

1

2016-07-06

C プログラミング入門

基幹7 (水5)

(2)

例題:多角形の面積

3

𝒑

0

𝒑

1

𝒑

2

𝒑

3

𝒑

4

𝑆

𝑆

= ෍

𝑖=0

𝑛−1

𝑇

𝑖

= ෍

𝑖=0

𝑛−1

1

2

𝒑

𝑖

𝒑

𝑖+1

𝑇

0

𝜃

𝟎

where 𝒑

𝑛

= 𝒑

0

この計算は、𝑇𝑖 を符号付き面積とし、頂点の順に 番号付けを行うことで、原点の位置によらない。 二次元の位置ベクトル n = 5 (5角形) の例 原点 (ゼロベクトル)

(3)

4

例題:多角形の面積

// n 個の x 座標列 px と, y 座標列 py の作る多角形の面積 // ただし、点は左回りに並んでいるとする

double PolygonArea(double *px, double *py, int n) {

double S = 0.0; int i; for(i = 0; i < n; ++i) {

S += TriangleArea(px[i], py[i], px[(i+1)%n], py[(i+1)%n]); } return S; } 1.0 −2.4 −5.0 −1.5 2.0 1.0 1.3 −0.2 −2.0 −0.3 *px *py double px[] と書いてもよい 1 つ目の点の座標 i == n-1 の時、 0 となる

(4)

5

例題:多角形の面積

// 3点 (0,0), (x1, y1), (x2, y2) のなす三角形の面積 // 時計回りに並んでいる場合正、そうでない場合は負となる

double TriangleArea(double x1, double y1, double x2, double y2) {

return (x1*y2 - x2*y1) / 2.0; }

𝒑

1

= 𝑥

1

, 𝑦

1

𝑇

𝜃

𝟎

𝒑

2

= 𝑥

2

, 𝑦

2

𝑇 =

1

2

𝒑

1

𝒑

2

sin 𝜃 =

1

2

𝒑

1

𝒑

2

=

1

2

𝑥

1

𝑦

2

− 𝑥

2

𝑦

1

(5)

観察ポイント

点の座標 x, y がバラバラに扱われている

関数の引数の数が多い

点を表す新しい型を作れないか?

たとえば…

6

例題:多角形の面積

引数の総数が 減っている バラバラに扱う場合 POINT という型で表した場合 double PolygonArea(double *px,

double *py, int n);

double PolygonArea(

POINT

*

p

,

int n);

double TriangleArea(double x1,

double y1, double x2, double y2);

double TriangleArea(

POINT p1

,

(6)

複数の変数をまとめて一つの変数として扱う

方法

int や double といった型と同じように使用

可能

7

構造体の概要

typedef struct { double x, y; } Point; 構造体 Point 型の定義 { double S; Point P1 = { 1, 2 }; Point P2 = { -2, 1 }; S = TriangleArea(P1, P2); ... 構造体 Point 型を使ったコード

今日の目標:

このようなコードを書けるようになること

(7)

構造体の定義

構造体型の変数と typedef

構造体の初期化

構造体の読み書き

構造体のコピー

関数による構造体の扱い

構造体へのポインタ

8

構造体の説明目次

(8)

構造体

は一つのメモリ領域に複数の変数を格

納する

それぞれをメンバ

(member)

という

9

構造体の定義

struct

TagName

{

int x, y;

double a, b, c;

}

;

キーワード 構造体を区別するためのタグ名 任意個数のメンバ変数 セミコロンで終わる

(9)

型名として "

struct TagName

" を使う

10

構造体型の変数

... struct Point { double x, y; }; int main(void) { struct Point p1, p2; ... double x double y struct Point p1 double x double y struct Point p2 メモリ上のレイアウ ト(順番や隙間の大 きさ)は環境による 「struct + タグ名」で 一つの型名を構成する

(10)

struct キーワードを付けるのは面倒なので、

多くの場合 typedef を使って別名を定義

11

構造体型の変数

...

typedef struct Point

{ double x, y; } Point; int main(void) { struct Point p1; Point p2; ... double x double y struct Point p1 double x double y struct Point p2 1 単語で型名を書ける 構造体型を Point とい う型名として定義する タグ名と型名は同じで構わない タグ名を _Point や Point_tag の様 に区別する流儀もある また、構造体の別名をすべて大文字 で表す流儀もある この場合、タグ名を省略してもよい

(11)

構造体の初期化は、配列と似た記法を用いる

初期化をしない自動変数の値は不定

12

構造体の初期化

... typedef struct { double x, y; } Point; int main(void) { Point p1 = { 1.0, 2.0 }; Point p2; ... 1.0 double x 2.0 double y struct Point p1 ? double x ? double y struct Point p2 通常の変数と同様 メンバのかかれて いる順に与える 初期化が一部のみ指 定されている場合は、 残りはすべてゼロと なる

(12)

同じ構造体の別の変数を与えることにより、

すべてのメンバのコピーで初期化する

13

構造体の初期化

... typedef struct { double x, y; } Point; int main(void) { Point p1 = { 1.0, 2.0 }; Point p2 = p1; ... 1.0 double x 2.0 double y struct Point p1 1.0 double x 2.0 double y struct Point p2 配列との大きな違い すべてのメンバが コピーされる

(13)

構造体のメンバは

.

演算子を使う

14

構造体のアクセス

... typedef struct { double x, y; } Point; 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); ... p1=(1.000000,2.000000) p1=(-2.000000,2.000000) 1.0 double x 2.0 double y struct Point p1 メンバ変数を読む メンバ変数に代入する 出力

(14)

構造体は代入演算子によりコピーできる

15

構造体のコピー

... typedef struct { double x, y; } Point; int main(void) { Point p1 = { 1.0, 2.0 }; Point p2 = p1; Point p3; p3 = p1; ... 初期化のイコール 代入演算子によるコピー

(15)

構造体は関数の引数、戻り値として使える

16

関数で構造体を扱う

... 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; } int main(void) { Point st = { 1.0, 0.0 }; Point ed = { 2.0, 2.0 }; Point md; md = midpoint(st, ed); ... 仮引数へコピーされる 戻り値がコピーされる 配列と異なる 実引数がコピーが 渡される

(16)

構造体のメンバに配列を持たせることで、配

列のコピーを行うこともできる

17

構造体の配列メンバ

... typedef struct {

char name[20]; int age;

} Person; int main(void) { Person X = { "Taro", 22 }; Person Y; Y = X; Y.name = X.name; "Taro" char name[20] 22 int age Person X "Taro" char name[20] 22 int age Person Y 配列のコピー 配列の直接のコピーは できない 配列を含む構造体自体 のコピーは可能

(17)

システムのメモリ領域

ポインタ変数は単純にアドレス値のコピー

ポインタの先はコピーしないので浅いコピー

(shallow copy)

と呼ばれる

18

構造体のポインタ変数メンバ

typedef struct {

char *name; int age;

} Person; int main(void) { Person X = { "Taro", 22 }; Person Y; Y = X; Y.name = X.name; char *name 22 int age Person X char *name 22 int age Person Y アドレスのコピー この代入と同等 中のポインタはアドレ スがコピーされる "Taro" 配列ではなくポインタ

(18)

変数の種類 宣言 初期化 代入演算子に よるコピー 基本型 int x; = 25; 可能 配列 文字配列 int a[10]; = { 1, 2, 3 }; 不可能 char s[256]; = "string literal";

構造体 struct Point P; Point P; = { 20, 30 }; = Q; (Point構造体の別の変数) 可能 (内容全体) ポインタ変数 int *p; = &var; 可能

(アドレス値)

char *pstr; = "string literal";

19

変数の比較

構造体のサイズが大きい場合はコピー に時間がかかることに注意 アドレス演算子や malloc() の戻り値からアドレスを得る

typedef struct Point {...} Point;

を定義した場合

(19)

以下の場合に使われる

構造体を直接変更する必要がある場合

構造体のコピーに時間がかかるのを避けたい場合

20

構造体へのポインタ

... // 点の座標を原点に変更する

void setOrigin(Point *p) { (*p).x = 0; (*p).y = 0; } ポインタ p の内容を読むために、まず デリファレンス演算子 * が必要 p の指す構造体のメンバにアクセスす るために . 演算子を使うのだが、演算 子の優先順位の関係で括弧が必要とな る。

(20)

(*p).x という記述を簡略化するためにアロー

演算子 p

->

x が用意されている

ポインタ p が指す構造体のメンバを直接矢印で指

しているイメージ

21

構造体へのポインタ (アロー演算子)

... // 点の座標を原点に変更する

void setOrigin(Point *p) {

p->x = 0;

p->y = 0;

}

(21)

通常の配列と同じ

動的メモリで確保する場合は sizeof を使う

22

構造体の配列・動的メモリ

... { int i; Point points[10]; for(i = 0; i < 10; ++i) { points[i].x = i; points[i].y = i*i; ... ... { int i;

Point *points=malloc(sizeof(Point)*10);

for(i = 0; i < 10; ++i) { points[i].x = i; points[i].y = i*i; ... 10 要素の配列変数の場合 10 要素の動的メモリの場合 添え字演算子の優先順位のほうが高い

(22)

関連する情報を一つにまとめることができる

他の変数と同じように扱える

関数などでのやり取りが簡潔になる

ポインタを介した操作が(さらに)理解しに

くい

普通の変数なら

.

演算子

ポインタ変数なら

->

演算子

23

構造体の利点・欠点

(23)

<stdio.h> の FILE

OpenCV

(一般のライブラリの一つ) の

CvPoint, CvSize, CvRect など

秋期「C プログラミング」で扱うリンクリス

トや木のようなデータ構造

C++ では変数だけでなく関数も持てるよう

に拡張されている (クラスと呼ばれる)

24

構造体の例

(24)

構造体の変数を以下の構文で直接定義するこ

とができるが、あまり使用されない。

struct

TagName { ... }

varname

;

今までの例では最後の変数名を省略し、型の定義

のみを行っている

構造体にビット単位でアクセスすることを可

能にするビットフィールドという機能がある

講義では省略する

メモリ効率やバイナリレベルでの互換性のために

使われるが、通常は使用しない

25

構造体の細かい話 (1)

(25)

C99 での新機能

メンバ名を指定した初期化

(Designated Initializer)

• 例: Point P = {

.y

= 10,

.x

= 20 };

最後のメンバとしてサイズを指定しない配列が書

ける (0長配列)

構造体をリテラルとして書く複合リテラル

(compound literal)

26

構造体の細かい話 (2)

参照

関連したドキュメント

Then he found that the trapezoidal formula is optimal in each of both function spaces and that the error of the trapezoidal formula approaches zero faster in the function space

Since locally closed functions with all point inverses closed have closed graphs [2], (c) implies

Our first result is a lattice path interpretation of the double Schur function based on a flagged determinantal formula derived from a formula of Lascoux for the symmetric

By performing center manifold reduction, the normal forms on the center manifold are derived to obtain the bifurcation diagrams of the model such as Hopf, homoclinic and double

We define the elliptic Hecke algebras for arbitrary marked elliptic root systems in terms of the corresponding elliptic Dynkin diagrams and make a ‘dictionary’ between the elliptic

In Section 4, we use double-critical decomposable graphs to study the maximum ratio between the number of double-critical edges in a non-complete critical graph and the size of

 我が国における肝硬変の原因としては,C型 やB型といった肝炎ウイルスによるものが最も 多い(図

[Horizontal dual of Theorem 5.7.] Let T be a normal pseudo double monad on a double category K, and assume that T is pointwise left exact... Briefly, the features of double