応用プログラミング 第 10 回 構造体 2017 年 11 月 22 日 ( 水 )
第 11 章 構造体
構造体 * 国民の個人情報を管理したい例 : マイナンバー (id), 名前 (na), 年齢 (ag) * 管理する方法は? 配列を用いる方法 ただし, 年齢などでソートするとき面倒 id[0] id[1] id[2] id[3] id[4] na[0] na[1] na[2] na[3] na[4] ag[0] ag[1] ag[2] ag[3] ag[4] ここも合わせてソート ここも合わせてソート ここをソートしたら
構造体 * 国民の個人情報を管理したい例 : マイナンバー (id), 名前 (name), 年齢 (age) * 構造体を用いて新しい型を作成する マイナンバー 名前 年齢の情報をもつ新しい Nation 型を作成 構造体は複数の情報を一つの型として扱える Nation 型 id name age Nation 型の配列 Na Na[0] Na[1] Na[2] Na[3] Na[4] id: 12345 name: Sasaki age: 30
struct Nation{ int id; string name; int age; ; 構造体の定義 (P.100) * 構造体を作る方法 struct によって定義する using namespace std; の直後 ( 関数の前 ) に定義するのが一般的 struct Nation{ int id; string name; int age; x, y; // Nation 型の変数 x, y struct 型の名前 { 必要なデータ型の宣言 自作する型の名前は先頭を大文字にするのが良い ( 通常の変数型と区別するため ) { の後ろのセミコロン ; を忘れないこと! ( ; の前に変数を書くと, 同時に宣言もできる仕様なので何も宣言しない場合でも ; が必要. 空文のような扱い.)
構造体の基本 (P.101) * 定義した構造体を使用する方法 Nation x; x.id = 12345; x.name = Sasaki ; x.age = 30; id: 12345 name: Sasaki age: 30 構造体の変数宣言 : 構造体の名前 + 変数名 構造体の各要素にアクセス : 変数名 +.( ドット ) + 要素名 * あとは基本的に普通のデータ型と同様に扱える
構造体の基本 (P.101) Staff{ string name; int age; ; // 実行結果 John 33 John 33 int main(){ Staff x; x.name = John ; x.age = 33; cout << x.name << << x.age << endl; Staff y = x; cout << y.name << << y.age << endl; return 0;
構造体の基本 (P.101) // Staff 構造体の定義は先ほどと同じ int main(){ Staff list[3]; list[0].name = Tom ; list[0].age = 29; list[1].name = Bob ; list[1].age = 31; cin >> list[2].name >> list[2].age; for(int i = 0; i < 3; i++){ cout << list[i].name << << list[i].age << endl; return 0; // 実行結果 Alice 25 Tom 29 Bob 31 Alice 25
構造体変数の初期化 (P.102) * 配列のようにして初期化する 構造体の各要素 ( メンバ ) の型に注意して初期化値を決める Staff x; x.name = Sasaki ; x.age = 30; Staff x = { Sasaki, 30; Staff の要素は string 型, int 型の順 * 構造体の配列の初期化は 2 次元配列のときに似ている Staff member[2] = {{ Sasaki, 30, { Tom, 29;
構造体変数の初期化 (P.102) // Staff 構造体の定義は同じ int main(){ Staff x = { Bob, 31; Staff y = x; // xが持つ値を初期化値に指定 Staff list[3] = {x, y, { Sasaki, 30; for(int i = 0; i < 3; i++){ cout << list[i].name << << list[i].age << endl; return 0; // 実行結果 Bob 31 Bob 31 Sasaki 30 Staff 型の 1 次元配列を宣言 初期化
構造体 配列を要素にもつ構造体 (P.103) * 構造体は任意の型の要素を持てる ( 例 : 配列, 別の構造体, etc.) 異なる構造体の中では同じ変数名を使ってよい ( 関数と同じ ) struct Point{ string name; // 点の名前 int x, y; // x, y 座標 ; // 定義 0 y ΔABC C:(1,1) A:(2,3) B:(4,2) x struct Triangle1{ string name; // 三角形の名前 Point a, b, c; // 三角形の頂点 ; // 定義 1 struct Triangle2{ string name; // 三角形の名前 Point abc[3]; // 三角形の頂点 ; // 定義 2
// 定義 0 // 定義 1 構造体 配列を要素にもつ構造体 (P.103) // 実行結果 T1:{A(2, 3), B(4, 2), C(1, 1) int main(){ Triangle1 t = { T1, { A, 2, 3, { B, 4, 2, { C, 1, 1; cout << t.name << :{ << t.a.name << ( << t.a.x <<, << t.a.y << ) <<, << t.b.name << ( << t.b.x <<, << t.b.y << ) <<, << t.c.name << ( << t.c.x <<, << t.c.y << ) << endl; return 0; t.name : Triangle1 型変数 t の name( 名前 ) にアクセス t.a.name : t がもつ Point 型変数 a の name( 名前 ) にアクセス t.a.x : t がもつ Point 型変数 a の x(x 座標 ) にアクセス t.a.y : t がもつ Point 型変数 a の y(y 座標 ) にアクセス
// 定義 0 // 定義 2 構造体 配列を要素にもつ構造体 (P.103) // 実行結果 T1:{A(2, 3), B(4, 2), C(1, 1) int main(){ Triangle2 t = { T1, {{ A, 2, 3, { B, 4, 2, { C, 1, 1; cout << t.name << :{ ; for(int i = 0; i < 3; i++){ cout << t.abc[i].name << ( << t.abc[i].x <<, << t.abc[i].y << ) ; if(i!= 2) cout <<, ; cout << << endl; return 0; t.abc[i].name : t がもつ Point 型配列 abc の i 番目の name( 名前 ) t.abc[i].x : t がもつ Point 型配列 abc の i 番目の x(x 座標 ) t.abc[i].y : t がもつ Point 型配列 abc の i 番目の y(y 座標 )
構造体の関数での利用 (P.104) * 構造体は一つの変数型として扱える 関数の仮引数や戻り値としても指定できる 他の変数同様, 関数内で値を変更しても main 関数方には影響なし // 構造体 Point の定義 Struct Point{ string name; int x, y; ; 構造体を用いることにより, return 文で一度に複数の情報を送ることができる 関数内で変更しても main 関数 ( 呼び出し元 ) の変数には影響しない // 返却値が Point 型の関数 input Point input ( ){ Point p = { noname, 0, 0; cin >> p.name <<, << p.x <<, << p.y << endl; return p; //Point 型の仮引数をもつ output 関数 void output(point k){ cout << k.name <<, << k.x <<, << k.y << endl; k.name = done ;
Struct Point{ string name; int x, y; ; 構造体の関数での利用 (P.104) Point input ( ){ Point p = { noname, 0, 0; cin >> p.name <<, << p.x <<, << p.y << endl; return p; void output(point k){ cout << k.name <<, << k.x <<, << k.y << endl; k.name = done ; // 左からの続き int main(){ Point a = input(); output(a); cout << a.name << endl; const int N = 2; Point b[n]; for(int i = 0; i < N; i++){ b[i] = input(); output(b[i]); return 0; // 実行例 A 1 2 A, 1, 2 A B 3 0 B, 3, 0 // 以下略
構造体のリファレンス引数 (P.105) * 構造体をリファレンス引数で関数に渡すことが可能 * 構造体のリファレンス引数に対しては, 2 通りの使われ方がある : ( 左 ) 関数内での値の更新を呼び出し元の関数にも反映させるとき ( 右 ) サイズが大きい構造体を引数として取るとき void update(point& p){ p.x += 2; p.y += 3; void print(const Point& p){ cout << p.name <<, << p.x <<, << p.y << endl; 右の場合は, 値の変更を目的としていないので, 習慣的に const 宣言を行う ( 値の変更をさせないようにする )
構造体のリファレンス引数 (P.105) struct Point{ string name; int x, y; ; int main(){ Point a = { X, 1, 2; update(a); print(a); void update(point& p){ p.x += 2; p.y += 3; void print(const Point& p){ cout << p.name <<, << p.x <<, << p.y << endl; const int N = 2; Point b[n] = {{ A, 3, 4, { B, 5, 6; for(int i = 0; i < N; i++){ update(b[i]); return 0; // 実行結果 X, 3, 5 A, 3, 4 B, 5, 6
構造体のポインタ引数 (P.106) * 構造体をポインタ引数で関数に渡すことが可能 * ポインタとして構造体の要素にアクセスする方法が 2 通りある : ( 左 ) 間接演算子 * を用いる方法 ( 右 ) アロー演算子 -> を用いる方法 void update(point *p){ if(p!= 0){ (*p).x += 2; (*p).y += 3; 同値 void update(point *p){ if(p!= 0){ p -> x += 2; p -> y += 3; もしポインタ p が何も指し示していなければ, プログラムが異常を起こすので, p が NULL ポインタかどうかを確認 (p!= 0)
構造体のポインタ引数 (P.106) struct Point{ string name; int x, y; ; void update(point *p){ if(p!= 0){ p -> x += 2; // (*p).x += 2; p -> y += 3; // (*p).y += 3; int main(){ Point a = { X, 1, 2; update(&a); print(&a); const int N = 2; Point b[n] = {{ A, 3, 4, { B, 5, 6; for(int i = 0; i < N; i++){ print(&b[i]); void print(const Point *p){ if(p!= 0){ cout << p -> name <<, << p -> x <<, << p -> y << endl; return 0; // 実行結果 X, 3, 5 A, 3, 4 B, 5, 6
配列の情報をまとめる構造体 (P.107) * 配列と配列の要素数を一つの構造体にする 配列内の要素を出力 配列のどこまで値が入っているかが重要 構造体にして, 配列とその要素数をひとまとめにする const int N = 10; // 大域変数 struct MyArray{ int size; double d[n]; ; ( 例 ) MyArray k = {3, {1.1, 2.2, 3.3; k.size に要素数 3 が格納されているので, 関数には k を渡すだけで良い.
配列の情報をまとめる構造体 (P.107) const int N = 10; // 大域変数 Struct MyArray{ int size; double d[n]; ; void print(const MyArray& a){ for(int i = 0; i < a.size; i++) cout << a.d[i] << ; cout << endl; void add(myarray& a, double d){ if(a.size < N){ a.d[a.size] = d; a.size++; int main(){ MyArray x = {5, {1.2, 2.3, 3.4, 4.5, 5.6; print(x); for(double i = 1.0; i < 10.0; i++) add(x, i); print(x); return 0; // 実行結果 1.2 2.3 3.4 4.5 5.6 1.2 2.3 3.4 4.5 5.6 1.0 2.0 3.0 4.0 5.0
練習問題 問題. 人名と好きな果物 ( 高々 3 つ ) を格納する配列の組を保存するための Pfruit 構造体を宣言し, ある果物を入力すると, その果物が好きな人物の名前を列挙するプログラムを作成せよ. ただし, 入力した果物が好きな人がいない場合は, Not exist と出力せよ. // 3 人の名前と好きな果物リスト Alice : banana, apple, orange Bob : apple, strawberry Tom : apple, orange // 実行例 1 Input a fruit: orange Alice, Tom // 実行例 2 Input a fruit: apple Alice, Bob, Tom // 実行例 3 Input a fruit: pineapple Not exist.