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

概要 プログラミング論 構造体 構造体 複数の変数を組合わせて, ひとまとめにしたもの 簡単 重要 自己参照型, リスト 重要, 難しい S-1 S-2 新しい構造体の宣言 struct 構造体名 { データ型メ

N/A
N/A
Protected

Academic year: 2021

シェア "概要 プログラミング論 構造体 構造体 複数の変数を組合わせて, ひとまとめにしたもの 簡単 重要 自己参照型, リスト 重要, 難しい S-1 S-2 新しい構造体の宣言 struct 構造体名 { データ型メ"

Copied!
7
0
0

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

全文

(1)

プログラミング論

構造体

http://www.ns.kogakuin.ac.jp/~ct13140/Prog/

S-1

概要

• 構造体

– 複数の変数を組合わせて,ひとまとめにしたもの

– 簡単

– 重要

• 自己参照型, リスト

– 重要, 難しい

S-2

新しい構造体の宣言

struct 構造体名{

データ型 メンバ名1;

データ型 メンバ名2;

:

データ型 メンバ名3;

};

S-3

struct seiseki{

char name[20];

int

math;

int

engl;

};

これで「struct seiseki型」

という新しい型が作成(宣言)された.

mathやenglは構造体のメンバ.

構造体型の変数の宣言

struct seiseki{

char name[20];

int math;

int engl;

};

void main(){

struct seiseki sane, yasu;

:

}

S-4

「struct seiseki」が型.

sane や yasu が変数名.

int x, y;

なら,intが型,

xやyが変数名.

yasuという1個の変数の中に,

char name[20],

int math, int englの

3個が含まれている.

メンバ変数へのアクセス

• (1) ドット(dot)演算子

.

– 構造体変数

.

メンバ変数

• 構造体変数aの中の,メンバ変数xにアクセス

a.x

• 構造体へのポインタpが指す構造体の中の,メンバ変

数yにアクセス

(*p).y

• (2) アロー(arrow)演算子

->

– 構造体変数へのポインタ

->

メンバ変数

• 構造体へのポインタpが指す構造体の中の,メンバ変

数zにアクセス

p->z

S-5

メンバ変数へのアクセス

(1) ドット演算子 (非ポインタ)

struct seiseki{ char name[20]; int math; int engl; }; void main(){

struct seiseki sane, yasu; sane.math = 80;

sane.engl = 70;

strcpy( sane.name, "saneyasu"); sane.name[0] = 'S';

printf("name=%s, math=%d¥n", sane.name, sane.math);

} S-6 実行結果 name=Saneyasu, math=80 (1) ドット演算子 . 構造体変数 . メンバ変数

メンバ変数へのアクセス

(1) ドット演算子 (ポインタ)

struct seiseki{ char name[20]; int math; int engl; }; void main(){

struct seiseki sane, yasu; struct seiseki *p;

p = &yasu; // アドレス1028~1031に1000が格納される

yasu.math = 50; // アドレス1020~1023に50が格納される (*p).engl = 60;

printf("math=%d, engl=%d¥n", (*p).math, yasu.engl);

}

S-7 実行結果

math=50, engl=60

1000 1019 1020 1021 1022 1023 1024 1025 1026 1027

char name[20] int math int engl struct seiseki *p 1028 1029 1030 1031 (1) ドット演算子 . 構造体変数 . メンバ変数 yasu

メンバ変数へのアクセス

(1) ドット演算子 (ポインタ)

struct seiseki{ char name[20]; int math; int engl; }; void main(){

struct seiseki sane, yasu;

printf("%p %p¥n", &sane, &(sane.name[0])); printf("%p %p¥n", &(sane.math), &(sane.engl)); }

S-8 実行結果

0xbfb2ca18 0xbfb2ca18 0xbfb2ca2c 0xbfb2ca30

ca18 ca2b ca2c ca2d ca2e caef ca30 ca31 ca32 ca33

char name[20] int math int engl

(1) ドット演算子 . 構造体変数 . メンバ変数

(2)

メンバ変数へのアクセス

(1) ドット演算子 (ポインタ)

struct seiseki{ char name[20]; int math; int engl; }; void main(){

struct seiseki sane, yasu; struct seiseki *p; p = &yasu; (*p).math = 70; // OK *p.engl = 80; // NG! *(p.engl)=80 の意味になってしまう! } S-9 (1) ドット演算子 . 構造体変数 . メンバ変数

演算子の

優先順位に注意!

メンバ変数へのアクセス

(2) アロー演算子 (ポインタ)

struct seiseki{ char name[20]; int math; int engl; }; void main(){

struct seiseki sane, yasu; struct seiseki *p;

p = &sane;

strcpy(p->name, "saneyasu"); p->math=10; p->engl=20; p->name[0] = 'S';

printf("name=%s, math=%d¥n", p->name, p->math);

} S-10 実行結果 name=Saneyasu, math=10 (2) アロー演算子 -> 構造体変数へのポインタ -> メンバ変数 p->xyz と (*p).xyz は同義

メンバ変数へのアクセス (ポインタ)

ドット演算子 と アロー演算子

struct seiseki sane, yasu;

struct seiseki *p;

p = &sane;

(*p).math = 70;

p->engl = 80;

S-11 ポインタの場合, (*p).math = 70; p->math = 70; のどちらを用いても問題ない. 通常 -> を用いる. アロー演算子は, シンタックスシュガー

構造体の宣言

(1)一般的(?)な構造体宣言と変数宣言 struct hoge{ int x; char y; }; void main(){ struct hoge z; } S-12

構造体の宣言

(2)構造体宣言と変数宣言を別々に struct hoge{ int x; char y; }; struct hoge a, b; void main(){ } struct hogeが型名で, aとbが変数名. aとbはグローバル変数. S-13 (3)構造体宣言と変数宣言を同時に struct hoge{ int x; char y; } a, b; void main(){ } struct hogeが型名で, aとbが変数名. aとbはグローバル変数.

構造体の宣言

(3)再掲載 struct hoge{ int x; char y; } a, b; void main(){ } struct hogeが型名で, aとbが変数名. S-14 (4)名無し構造体 struct { int x; char y; } a, b; void main(){ } ・その構造体を二度と使わない ・変数を同時に宣言している 場合は,構造体名を省略可能

構造体の宣言

(5)typedefの使用 struct hoge{ int x; char y; };

typedef struct hoge sthoge;

void main(){ sthoge a, b; }

struct hoge型に, sthoge型という別名を与えた. struct hogeとsthogeが型名,

aとbが変数名.

S-15

構造体の宣言

(6)構造体宣言とtypedef同時使用

typedef struct hoge{ int x; char y; } sthoge; void main(){ sthoge a, b; } struct hogeとsthogeが型名, aとbが変数名. S-16 (7)構造体宣言とtypedef同時使用 typedef struct{ int x; char y; } sthoge; void main(){ sthoge a, b; } 名無し構造体にsthogeの別名を付与. sthogeが型名, aとbが変数名.

(3)

構造体の初期化

• (1)変数宣言と初期化を同時に struct seiseki{ char name[20]; int math; int engl; }; void main(){

struct seiseki sane={"SaneYama",10,20}; struct seiseki yasu={"Yasu",5};

printf("%s %d %d¥n",sane.name,sane.math,sane.engl); printf("%s %d %d¥n",yasu.name,yasu.math,yasu.engl); } S-17 実行結果 SaneYama 10 20 Yasu 5 0 {"Yasu",5,0} と同じ意味. 省略すると, 0になる. nameに"SaneYama"が, mathに10が, englに20が入る.

構造体の初期化

• (2)構造体宣言と変数宣言と初期化を同時に struct seiseki{ char name[20]; int math; int engl; } sane={"SaneYama",10,20}; void main(){ printf("%s %d %d¥n",sane.name,sane.math,sane.engl); } S-18 実行結果 SaneYama 10 20

構造体の初期化

• (3)配列 struct seiseki{ char name[20]; int math; int engl; }; void main(){

struct seiseki stu[10] = { {"Taro",10,20},

{"Jiro",30,40}, {"Saburo",50,60} };

printf("%s %d¥n", stu[1].name, stu[1].math); } S-19 実行結果 Jiro 30

構造体の初期化

• (4)配列 struct seiseki{ char name[20]; int math; int engl; }; void main(){

struct seiseki stu[10] = { "Taro",10,20,

"Jiro",30,40, "Saburo",50,60 };

printf("%s %d¥n", stu[1].name, stu[1].math); } S-20 実行結果 Jiro 30 データの対応が正しければ, 内側の括弧{}は 省略可能.

構造体の演算

• 構造体に対して,以下の演算が可能

(1) 代入 (構造体同士の丸ごとコピー)

(2) 構造体のアドレス取得

(3) 構造体のメンバを参照

(紹介済み)

• 構造体同士の比較はできない

– if( stu[0] < stu[1] ) はできない

– メンバ変数の比較は可能

S-21

構造体の演算 (1)

構造体同士のコピー

struct seiseki sane, yasu;

strcpy( sane.name, "Saneyasu"); sane.math=30;

sane.engl=40;

yasu = sane;

printf("%s %d¥n", yasu.name, yasu.engl);

S-22 struct seiseki{ char name[20]; int math; int engl; }; この代入で, saneの中身 (name,math,engl) のすべてが, yasuにコピーされる. name,math,englを 格納可能である入れ物が 2個作成される. 実行結果 Saneyasu 40

構造体の演算 (2)

アドレス獲得 &

&演算子で,構造体の先頭アドレスを取得可能 (以下,再掲載)

struct seiseki sane;

printf("%p %p¥n", &sane, &(sane.name[0])); printf("%p %p¥n", &(sane.math), &(sane.engl));

struct seiseki{ char name[20]; int math; int engl; }; 実行結果 0xbfb2ca18 0xbfb2ca18 0xbfb2ca2c 0xbfb2ca30

ca18 ca2b ca2c ca2d ca2e caef ca30 ca31 ca32 ca33

char name[20] int math int engl struct seiseki sane

S-23

構造体の演算 (2)

アドレス獲得 &

struct seiseki sane, stu[5];

struct seiseki *p, *q; p = &sane; // アドレスを取得し,pに代入 q = stu; // stu[0]のアドレスをqに代入 Printf("sizeof(struct seiseki)=%d", sizeof(struct seiseki)); printf("&sane=%p¥n", p); printf("stu =%p¥n", q); printf("stu+2=%p¥n", q+2); S-24 struct seiseki{ char name[20]; int math; int engl; }; 実行結果 sizeof(struct seiseki)=28 &sane=0x7fffa1018570 stu =0x7fffa10184e0

stu+2=0x7fffa1018518

4e0 4f3 4f4 f48

char name[20] int math int engl stu[0]

stu と stu+2の 差が 28*2=56

stu[1] 4fb 4fc

(4)

構造体とポインタ

struct seiseki stu[2]={

{"sane", 10, 20},{"yasu",30,40} };

struct seiseki *p;

p = stu; // pにstu[0]のアドレスを入れる

printf("stu[0] %d, stu[1] %d¥n",stu[0].math,stu[1].math);

p->math++; // stu[0]のmathを1増やす. 10から11に変わる.

printf("stu[0] %d, stu[1] %d¥n",stu[0].math,stu[1].math);

p++; // pを1増やす. pがstu[0]からstu[1]のアドレスになる.

p->math++; // stu[1]のmathを1増やす. 30から31に変わる.

printf("stu[0] %d, stu[1] %d¥n",stu[0].math,stu[1].math);

S-25 struct seiseki{ char name[20]; int math; int engl; }; 実行結果 stu[0] 10, stu[1] 30 stu[0] 11, stu[1] 30 stu[0] 11, stu[1] 31 p++ : pに格納されているアドレスが1増える. p->math++ : 「pに格納されているアドレス」に 存在する構造体の中のmathが1増える.

関数引数と構造体 (値渡し)

struct seiseki{ char name[20]; int math; int engl; };

void hoge(struct seiseki a){

printf("hoge: %d %d¥n", a.math, a.engl); a.math = 90;

a.engl = 95; }

void main(){

struct seiseki a = {"sane",10,20}; hoge(a);

printf("main: %d %d¥n", a.math, a.engl); } S-26 実行結果 hoge: 10 20 main: 10 20 aの中身は 変わっていない

関数引数と構造体 (アドレス渡し)

struct seiseki{ char name[20]; int math; int engl; };

void hoge(struct seiseki *a){

printf("hoge: %d %d¥n", a->math, a->engl); a->math = 90;

a->engl = 95; }

void main(){

struct seiseki a = {"sane",10,20}; hoge(&a);

printf("main: %d %d¥n", a.math, a.engl); } S-27 実行結果 hoge: 10 20 main: 90 95 aの中身は 変わっている

練習0

struct score{

int math;

int engl;

};

void hoge( struct score *s){

// s[0]の中のmathを100にする

}

void main(){

struct score s[100];

s[0].math=…; s[0].engl=…;

s[1].math=…; s[1].engl=…;

:

hoge( s );

}

S-28

練習1

struct score{

int math;

int engl;

};

void fuga( struct score *s){

// s[10]の中のmathを100にする

}

void main(){

struct score s[100];

s[0].math=…; s[0].engl=…;

s[1].math=…; s[1].engl=…;

:

hoge( s );

}

S-29

練習2

struct score{

int math;

int engl;

};

void piyo( struct score *s){

//すべて

(s[0]~s[99])

の中のmathを100に

}

void main(){

struct score s[100];

s[0].math=…; s[0].engl=…;

s[1].math=…; s[1].engl=…;

:

hoge( s );

}

S-30

正解 0, 1, 2

練習0 正解

(*s).math = 100;

または

s->math = 100;

以下はNG

*s.math = 100;

s.math = 100;

S-31

練習1 正解

(*(s+10)).math = 100;

または

(s+10)->math = 100;

以下はNG

(*s+10).math = 100;

*(s+10).math = 100;

*s+10.math = 100;

練習2 正解

int i;

for(i=0; i<100; i++){

(s+i)->math = 100;

}

入れ子構造体 (1)

構造体の中に構造体

struct score{ int math; int engl; }; struct student{ struct score sc; char name[20]; }; void main(){

struct student sane;

strcpy( sane.name, "Saneyasu");

sane.sc.math = 30; sane.sc.engl = 40; printf("%s %d¥n", sane.name, sane.sc.math); } S-32 実行結果 Saneyasu 30

構造体の中に,構造体

struct student型の変数saneの中に, struct score型の変数sがある. それは sane.sc である.

struct score型の sane.sc の中に, int型の変数mathがある.

それは sane.sc.math である.

これで,math,engl,name のすべての箱ができる.

(5)

入れ子構造体 (2a)

構造体の中に構造体へのポインタ

struct score{ int math; int engl; }; struct student{ struct score *p; char name[20]; }; S-33

構造体の中に,

構造体へのポインタ

void main(){

struct student sane; : } これで アドレスを入れる箱p と, charを20個入れるname ができる. intを入れるmath や intを入れるengl は作成されない. 104 123 100 101 102 103 name[20] p sane 105

入れ子構造体 (2a)

構造体の中に構造体へのポインタ

struct score{ int math; int engl; }; struct student{ struct score *p; char name[20]; }; S-34 実行結果 10 20 Sane

構造体の中に,

構造体へのポインタ

void main(){

struct student sane; struct score sc={10,20}; sane.p = &sc;

strcpy( sane.name, "Sane"); printf("%d¥n", sane.p->math); printf("%d¥n", sane.p->engl); printf("%s¥n", sane.name); } 104 123 100 101 102 103 saneはアドレスでないので sane. とする 'S' 124 name[20] p sane 'a' 124 125 126 127 128 129 130 131 10 math sc engl 20 105

入れ子構造体 (2b)

構造体の中に構造体へのポインタ

struct score{ int math; int engl; }; struct student{ struct score *p; char name[20]; }; S-35 実行結果 10 20 Sane

構造体の中に,

構造体へのポインタ

void main(){ struct score sc={10,20}; struct student sane; struct student *q; sane.p = &sc; q = &sane;

strcpy( q->name, "Sane"); printf("%d¥n", q->p->math); printf("%d¥n", q->p->engl); printf("%s¥n", q->name); } q はアドレスなので q-> とする 104 123 100 101 102 103 'S' 124 name[20] p sane 'a' 124 125 126 127 128 129 130 131 10 math sc engl 20 105

自己参照型構造体 (1)

#include <stdio.h> struct dp{ int data; struct dp *next; }; void main(){ struct dp a, b, c, d; a.data = 10; a.next = &b; b.data = 20; b.next = &c; c.data = 30; c.next = &d; d.data = 40; d.next = NULL; S-36 printf("%d ", a.data); printf("%d ", a.next->data); printf("%d ", a.next->next->data); printf("%d¥n", a.next->next->next->data); } 10 808 20 816 30 824 40 0 data next data next data next data next 800 804 808 812 816 820 824 828 a b c d 実行結果 10 20 30 40

自己参照型構造体 (1)

S-37 a data next 10 b data next 20 c data next 30 d data next 40 X

• リンクリスト

10 808 20 816 30 824 40 0 data next data next data next data next

800 804 808 812 816 820 824 828 a b c d

自己参照型構造体 (2)

#include <stdio.h> struct dp{ int data; struct dp *next; }; void main(){ struct dp a, b, c, d, *p; a.data = 10; a.next = &b; b.data = 20; b.next = &c; c.data = 30; c.next = &d; d.data = 40; d.next = NULL; S-38 p = &a; for(;;){ printf("%d ", p->data); p = p->next;

if( p == NULL ){ break; } } } 実行結果 10 20 30 40 これで p に 800(aのアドレス)が 代入される. 10 808 20 816 30 824 40 0 data next data next data next data next 800 804 808 812 816 820 824 828 a b c d

自己参照型構造体 (2)

#include <stdio.h> struct dp{ int data; struct dp *next; }; void main(){ struct dp a, b, c, d, *p; a.data = 10; a.next = &b; b.data = 20; b.next = &c; c.data = 30; c.next = &d; d.data = 40; d.next = NULL; S-39 p = &a; for(; p; ){ printf("%d ", p->data); p = p->next; } } 実行結果 10 20 30 40 これで, 「pがNULLでない間は 繰り返す」の意味になる. NULLと0はほぼ同じ. 10 808 20 816 30 824 40 0 data next data next data next data next

800 804 808 812 816 820 824 828 a b c d

自己参照型構造体 (2)

#include <stdio.h> struct dp{ int data; struct dp *next; }; void main(){ struct dp a, b, c, d, *p; a.data = 10; a.next = &b; b.data = 20; b.next = &c; c.data = 30; c.next = &d; d.data = 40; d.next = NULL; S-40 for(p=&a; p; ){ printf("%d ", p->data); p = p->next; } } 実行結果 10 20 30 40 もっと短くすることもできる. 10 808 20 816 30 824 40 0 data next data next data next data next 800 804 808 812 816 820 824 828

(6)

自己参照型構造体 (2)

#include <stdio.h> struct dp{ int data; struct dp *next; }; void main(){ struct dp a, b, c, d, *p; a.data = 10; a.next = &b; b.data = 20; b.next = &c; c.data = 30; c.next = &d; d.data = 40; d.next = NULL; S-41

for(p=&a; p; p = p->next){ printf("%d ", p->data); } } 実行結果 10 20 30 40 10 808 20 816 30 824 40 0 data next data next data next data next

800 804 808 812 816 820 824 828 a b c d もっと短くすることもできる.

自己参照型構造体 (3)

S-42 a data next 10 b data next 20 c data next 30 d data next 40 X a data next 10 b data next 20 c data next 30 d data next 40 X

リストからbを削除

a.next = a.next->next;

自己参照型構造体 (3)

#include <stdio.h> struct dp{ int data; struct dp *next; }; void main(){ struct dp a, b, c, d, *p; a.data = 10; a.next = &b; b.data = 20; b.next = &c; c.data = 30; c.next = &d; d.data = 40; d.next = NULL; S-43 a.next = a.next->next; p = &a; for(;;){ printf("%d ", p->data); p = p->next;

if( p == NULL ){ break; } }

}

実行結果 10 30 40

10 816 20 816 30 824 40 0 data next data next data next data next

800 804 808 812 816 820 824 828 a b c d

自己参照型構造体 (4)

S-44 a data next 10 b data next 20 c data next 30 d data next 40 X a data next 10 b data next 20 c data next 30 d data next 40 X

bとcの順を交換

旧:a→b→c→d

新:a→c→b→d

自己参照型構造体 (4)

#include <stdio.h> struct dp{ int data; struct dp *next; }; void main(){ struct dp a, b, c, d, *p; a.data = 10; : d.next = NULL; p = &a; struct dp *x, *y, *z; x = p->next; y = p->next->next; z = p->next->next->next; S-45 x->next = z; y->next = x; p->next = y; for(;;){ printf("%d ", p->data); p = p->next;

if( p == NULL ){ break; } }

}

実行結果

10 30 20 40

10 816 20 824 30 808 40 0 data next data next data next data next

800 804 808 812 816 820 824 828 a b c d

自己参照型構造体 (5)

#include <stdio.h> struct dp{ int data; struct dp *prev; struct dp *next; }; void main(){ struct dp a, b, c, d, *p; a.data = 10; a.prev = NULL; a.next = &b; S-46 b.data = 20; b.prev = &a; b.next = &c; c.data = 30; c.prev = &b; c.next = &d; d.data = 40; d.prev = &c; d.next = NULL; p = &a; : }

双方向リスト

800 804 808 812 816 820 824 828 832 836 840 844 10 0 812 20 800 824 30 812 828 40 824 0 data prev next data prev next data prev next data prev next

a b c d

自己参照型構造体 (5)

S-47 a data 10 prev X next b data 20 prev next c data 30 prev next d data 40 prev next X

自己参照型構造体 (練習)

#include <stdio.h> struct dp{ int data; struct dp *prev; struct dp *next; }; void main(){ struct dp *p, *q; struct dp x; : p = …; q = p-> next; S-48

双方向リスト

/* p の次が q である. p と q の間に x を挿入するには? */ ??? ??? : }

(7)

自己参照型構造体 (練習)

S-49 a data 10 prev X next b data 20 prev next c data 30 prev next d data 40 prev next X a data 10 prev X next b data 20 prev next c data 30 prev next d data 40 prev next X x data 100 prev next (1) (2) (3) (4)

• cとdの間にxを挿入交換

旧:… b

←→

c

←→

d

新:… b

←→

c

←→

x

←→

d

自己参照型構造体 (正解)

#include <stdio.h> struct dp{ int data; struct dp *prev; struct dp *next; }; void main(){ struct dp *p, *q; struct dp x; : p = …; q = p-> next; S-50

双方向リスト

p->next = &x; /* (1) */ q->prev = &x; /* (2) */ x.prev = p; /* (3) */ /* x.prev = &p; は NG */ x.next = q; /* (4) */ /* x.next = &q; は NG */ : }

参照

関連したドキュメント

本プロジェクトでは、海上技術安全研究所で開発された全船荷重・構造⼀貫強度評価システム (Direct Load and Structural Analysis

事業区間の延長約 1.1km のうち、開削及びシールドトンネル構造が延長約 1.0km、擁壁構 造が延長約

建屋構造 鉄⾻造、鉄筋コンクリート、鋼板コンクリート等、遮蔽機能と⼗分な強度を有 する構造

参考第 1 表 中空断面構造物の整理結果(7 号炉 ※1 ) 構造物名称 構造概要 基礎形式 断面寸法

ろ過器等 を使用し 循環させ る場合の

1.6.1-3 に⽰すように、ハルモニタリング、データ同化、健全性評価の⼀連のフローからなる