プログラミング論 構造体 http://www.ns.kogakuin.ac.jp/~ct13140/Prog/ S-1 概要 • 構造体 – 複数の変数を組合わせて,ひとまとめにしたもの – 簡単 – 重要 • 自己参照型, リスト – 重要, 難しい S-2 新しい構造体の宣言 struct 構造体名{ データ型 メンバ名1; データ型 メンバ名2; : データ型 メンバ名3; }; 例 struct char int int }; seiseki{ name[20]; math; engl; これで「struct seiseki型」 という新しい型が作成(宣言)された. mathやenglは構造体のメンバ. S-3 構造体型の変数の宣言 struct seiseki{ yasuという1個の変数の中に, char name[20]; char name[20], int math; int math, int englの int engl; 3個が含まれている. }; void main(){ struct seiseki sane, yasu; : int x, y; } なら,intが型, 「struct seiseki」が型. xやyが変数名. sane や yasu が変数名. S-4 メンバ変数へのアクセス • (1) ドット(dot)演算子 . – 構造体変数.メンバ変数 • 構造体変数aの中の,メンバ変数xにアクセス a.x • 構造体へのポインタpが指す構造体の中の,メンバ変 数yにアクセス (*p).y • (2) アロー(arrow)演算子 -> – 構造体変数へのポインタ->メンバ変数 • 構造体へのポインタpが指す構造体の中の,メンバ変 数zにアクセス p->z S-5 メンバ変数へのアクセス (1) ドット演算子 (非ポインタ) (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); } 実行結果 name=Saneyasu, math=80 S-6 メンバ変数へのアクセス (1) ドット演算子 (ポインタ) (1) ドット演算子 . struct seiseki{ 構造体変数 . メンバ変数 char name[20]; 1000 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 int math; int engl; struct seiseki *p char name[20] int math int engl }; yasu void main(){ struct seiseki sane, yasu; struct seiseki *p; p = &yasu; // アドレス1000~1027に1000が格納される yasu.math = 50; // アドレス1020~1023に50が格納される (*p).engl = 60; printf("math=%d, engl=%d\n", (*p).math, yasu.engl); } 実行結果 math=50, engl=60 S-7 メンバ変数へのアクセス (1) ドット演算子 (ポインタ) (1) ドット演算子 . struct seiseki{ 構造体変数 . メンバ変数 char name[20]; ca18 ca2b ca2c ca2d ca2e caef ca30 ca31 ca32 ca33 int math; int engl; }; char name[20] int math int engl sane void main(){ struct seiseki sane, yasu; printf("%p %p\n", &sane, &(sane.name[0])); printf("%p %p\n", &(sane.math), &(sane.engl)); } 実行結果 0xbfb2ca18 0xbfb2ca18 0xbfb2ca2c 0xbfb2ca30 S-8 メンバ変数へのアクセス (1) ドット演算子 (ポインタ) (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 メンバ変数へのアクセス (2) アロー演算子 (ポインタ) (2) アロー演算子 -> struct seiseki{ 構造体変数へのポインタ -> メンバ変数 char name[20]; p->xyz と (*p).xyz は同義 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; name=Saneyasu, math=10 p->name[0] = 'S'; printf("name=%s, math=%d\n", p->name, p->math); } S-10 メンバ変数へのアクセス (ポインタ) ドット演算子 と アロー演算子 struct seiseki sane, yasu; struct seiseki *p; p = &sane; ポインタの場合, (*p).math = 70; (*p).math = 70; p->engl = 80; p->math = 70; のどちらを用いても問題ない. 通常 -> を用いる. アロー演算子は, シンタックスシュガー S-11 構造体の宣言 (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(){ (3)構造体宣言と変数宣言を同時に struct hoge{ int x; char y; } a, b; void main(){ } } struct hogeが型名で, aとbが変数名. aとbはグローバル変数. struct hogeが型名で, aとbが変数名. aとbはグローバル変数. S-13 構造体の宣言 (3)再掲載 struct hoge{ int x; char y; } a, b; void main(){ (4)名無し構造体 struct { int x; char y; } a, b; void main(){ } } ・その構造体を二度と使わない ・変数を同時に宣言している 場合は,構造体名を省略可能 struct hogeが型名で, aとbが変数名. S-14 構造体の宣言 (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が変数名. (7)構造体宣言とtypedef同時使用 typedef struct{ int x; char y; } sthoge; void main(){ sthoge a, b; } 名無し構造体にsthogeの別名を付与. sthogeが型名, aとbが変数名. S-16 構造体の初期化 nameに"SaneYama"が, • (1)変数宣言と初期化を同時に mathに10が, struct seiseki{ englに20が入る. char name[20]; {"Yasu",5,0} int math; と同じ意味. int engl; 省略すると, 0になる. }; 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); } 実行結果 SaneYama 10 20 Yasu 5 0 S-17 構造体の初期化 • (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); } 実行結果 SaneYama 10 20 S-18 構造体の初期化 • (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} Jiro 30 }; printf("%s %d\n", stu[1].name, stu[1].math); } S-19 構造体の初期化 • (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 Jiro 30 }; printf("%s %d\n", stu[1].name, stu[1].math); } S-20 構造体の演算 • 構造体に対して,以下の演算が可能 (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; saneの中身 (name,math,engl) yasu = sane; のすべてが, yasuにコピーされる. struct char int int }; seiseki{ name[20]; math; engl; name,math,englを 格納可能である入れ物が 2個作成される. printf("%s %d\n", yasu.name, yasu.engl); 実行結果 Saneyasu 40 S-22 構造体の演算 (2) アドレス獲得 & struct char int int }; &演算子で,構造体の先頭アドレスを取得可能 (以下,再掲載) struct seiseki sane; printf("%p %p\n", &sane, &(sane.name[0])); printf("%p %p\n", &(sane.math), &(sane.engl)); ca18 seiseki{ name[20]; math; engl; ca2b ca2c ca2d ca2e caef ca30 ca31 ca32 ca33 char name[20] int math int engl 実行結果 0xbfb2ca18 0xbfb2ca18 0xbfb2ca2c 0xbfb2ca30 struct seiseki sane S-23 構造体の演算 (2) アドレス獲得 & struct char int int }; seiseki{ name[20]; math; engl; struct seiseki sane, stu[5]; struct seiseki *p, *q; p = &sane; // アドレスを取得し,pに代入 q = stu; // stu[0]のアドレスをqに代入 printf("&sane=%p\n", p); 実行結果 printf("stu =%p\n", q); sizeof(struct seiseki)=28 &sane=0x7fffa1018570 printf("stu+2=%p\n", q+2); stu =0x7fffa10184e0 stu と stu+2の stu+2=0x7fffa1018518 差が 28*2=56 4e0 4f3 char name[20] 4f4 f48 int math stu[0] 4fb 4fc int engl stu[1] S-24 構造体とポインタ struct seiseki stu[2]={ struct seiseki{ char name[20]; {"sane", 10, 20},{"yasu",30,40} int math; }; int engl; 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); p++ : pに格納されているアドレスが1増える. p->math++ : 「pに格納されているアドレス」に 存在する構造体の中のmathが1増える. 実行結果 stu[0] 10, stu[1] 30 stu[0] 11, stu[1] 30 stu[0] 11, stu[1] 31 S-25 関数引数と構造体 (値渡し) 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の中身は a.engl = 95; 変わっていない } void main(){ 実行結果 struct seiseki a = {"sane",10,20}; hoge: 10 20 hoge(a); main: 10 20 printf("main: %d %d\n", a.math, a.engl); } S-26 関数引数と構造体 (アドレス渡し) 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の中身は a->engl = 95; 変わっている } void main(){ 実行結果 struct seiseki a = {"sane",10,20}; hoge: 10 20 hoge(&a); main: 90 95 printf("main: %d %d\n", a.math, a.engl); } S-27 入れ子構造体 (1) 構造体の中に構造体 struct score{ 構造体の中に,構造体 int math; struct student型の変数saneの中に, int engl; struct score型の変数sがある. }; それは sane.sc である. struct student{ struct score型の sane.sc の中に, struct score sc; int型の変数mathがある. char name[20]; それは sane.sc.math である. }; void main(){ これで,math,engl,name struct student sane; のすべての箱ができる. strcpy( sane.name, "Saneyasu"); sane.sc.math = 30; sane.sc.engl = 40; 実行結果 printf("%s %d\n", sane.name, sane.sc.math); Saneyasu 30 } S-28 入れ子構造体 (2a) 構造体の中に構造体へのポインタ 構造体の中に, 構造体へのポインタ void main(){ struct student sane; : } struct score{ int math; int engl; }; struct student{ struct score *p; char name[20]; }; 100 'S' 119 120 'a' name[20] sane 121 122 これで アドレスを入れる箱p と, charを20個入れるname ができる. intを入れるmath や intを入れるengl は作成されない. 123 124 125 126 127 128 129 130 124 10 20 p math engl sc 131 実行結果 10 20 Sane S-29 入れ子構造体 (2a) 構造体の中に構造体へのポインタ 構造体の中に, 構造体へのポインタ void main(){ struct student sane; struct score sc={10,20}; sane.p = ≻ strcpy( sane.name, "Sane"); printf("%d\n", sane.p->math); printf("%d\n", sane.p->engl); printf("%s\n", sane.name); } saneはアドレスでないので sane. とする struct score{ int math; int engl; }; struct student{ struct score *p; char name[20]; }; 100 'S' 119 120 'a' name[20] sane 121 122 123 124 125 126 127 128 129 130 124 10 20 p math engl sc 131 実行結果 10 20 Sane S-30 入れ子構造体 (2b) 構造体の中に構造体へのポインタ void main(){ struct score sc={10,20}; struct student sane; struct student *q; sane.p = ≻ q はアドレスなので q-> とする q = &sane; strcpy( q->name, "Sane"); printf("%d\n", q->p->math); printf("%d\n", q->p->engl); printf("%s\n", q->name); } 構造体の中に, 構造体へのポインタ struct score{ int math; int engl; }; struct student{ struct score *p; char name[20]; }; 100 'S' 119 120 'a' name[20] sane 121 122 123 124 125 126 127 128 129 130 124 10 20 p math engl sc 131 実行結果 10 20 Sane S-31 自己参照型構造体 (1) 800 804 808 812 816 820 824 828 10 808 20 816 30 824 40 0 #include <stdio.h> data next data next data next data next struct dp{ a b c d int data; struct dp *next; }; void main(){ struct dp a, b, c, d; a.data = 10; a.next = &b; b.data = 20; printf("%d ", a.data); b.next = &c; printf("%d ", a.next->data); c.data = 30; printf("%d ", a.next->next->data); c.next = &d; printf("%d\n", a.next->next->next->data); d.data = 40; } 実行結果 d.next = NULL; 10 20 30 40 S-32 自己参照型構造体 (1) 800 804 808 812 816 820 824 828 10 808 20 816 30 824 40 0 data next data next data next data next a a data next b b 10 data next c d c 20 data next d 30 data 40 next • リンクリスト S-33 自己参照型構造体 (2) 800 804 808 812 816 820 824 828 10 808 20 816 30 824 40 0 #include <stdio.h> data next data next data next data next struct dp{ a b c d int data; struct dp *next; }; これで p に void main(){ 800(aのアドレス)が struct dp a, b, c, d, *p; 代入される. a.data = 10; p = &a; a.next = &b; for(;;){ b.data = 20; printf("%d\n", p->data); b.next = &c; p = p->next; c.data = 30; if( p == NULL ){ break; } c.next = &d; } d.data = 40; 実行結果 } d.next = NULL; 10 20 30 40 S-34 自己参照型構造体 (4) a data b 10 next data c 20 next data d 30 next data 40 next リストからbを削除 a.next = a.next->next; a data next b 10 data next c 20 data next d 30 data 40 next S-35 自己参照型構造体 (4) 800 804 808 812 816 820 824 828 10 816 #include <stdio.h> data next struct dp{ a 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; 20 816 30 824 40 0 data next data next data next b c d a.next = a.next->next; p = &a; for(;;){ printf("%d\n", p->data); p = p->next; if( p == NULL ){ break; } } } 実行結果 10 30 40 S-36 自己参照型構造体 (5) 800 804 808 812 816 820 824 828 10 808 #include <stdio.h> data next struct dp{ a 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; b.data = 20; b.prev = &a; b.next = &c; c.data = 30; 20 816 30 824 40 0 data next data next data next b c d 双方向リスト c.prev = c.next = d.data = d.next = p = &a; : &b; &d; 40; NULL; } S-37
© Copyright 2024 ExpyDoc