応用プログラミング
第10回
構造体
構造体
*国民の個人情報を管理したい
例: マイナンバー(id), 名前(na), 年齢(ag) *管理する方法は?
配列を用いる方法
ただし, 年齢などでソートするとき面倒
na[0] na[1] na[2] na[3] na[4]
ag[0] ag[1] ag[2] ag[3] ag[4]
id[0] id[1] id[2] id[3] id[4]
ここをソートしたら…
ここも合わせてソート ここも合わせてソート
構造体
*国民の個人情報を管理したい
例: マイナンバー(id), 名前(name), 年齢(age)
*構造体を用いて新しい型を作成する
マイナンバー・名前・年齢の情報をもつ新しいNation型を作成 構造体は複数の情報を一つの型として扱える
Na[0] Na[1] Na[2] Na[3] Na[4] Nation型 id name age Nation型の配列Na id: 12345 name: Sasaki age: 30
構造体の定義(P.100)
struct
Nation{
int id;
string name;
int age;
}
;
*構造体を作る方法 structによって定義する using namespace std; の直後(関数の前)に定義するのが一般的 struct 型の名前{必要なデータ型の宣言} 自作する型の名前は先頭を大文字にするのが良い(通常の 変数型と区別するため) { }の後ろのセミコロン「;」を忘れないこと! (「;」の前に変数を書くと, 同時に宣言もできる仕様なので何 も宣言しない場合でも「;」が必要.空文のような扱い.)struct
Nation{
int id;
string name;
int age;
} x, y
;
// Nation型の変数x, y構造体の基本(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; }; 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;
}
// 実行結果 John 33
構造体の基本(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 struct Triangle1{ string name; // 三角形の名前 Point a, b, c; // 三角形の頂点 }; // 定義1 struct Triangle2{ string name; // 三角形の名前 Point abc[3]; // 三角形の頂点 }; // 定義2 y x B:(4,2) ΔABC C:(1,1) A:(2,3)構造体・配列を要素にもつ構造体(P.103)
// 定義0 // 定義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座標)にアクセス
// 実行結果
構造体・配列を要素にもつ構造体(P.103)
// 定義0 // 定義2 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; } // 実行結果 T1:{A(2, 3), B(4, 2), C(1, 1)}
t.abc[i].name : t がもつPoint型配列abc のi番目の name(名前) t.abc[i].x : t がもつPoint型配列abc のi番目の x(x座標)
構造体の関数での利用(P.104)
*構造体は一つの変数型として扱える 関数の仮引数や戻り値としても指定できる 他の変数同様, 関数内で値を変更してもmain関数方には影響なし //構造体Pointの定義 Struct Point{ string name; int x, y; }; //Point型の仮引数をもつoutput関数void output(Point k){
cout << k.name << “, ” << k.x << “, ” << k.y << endl; k.name = “done”; } //返却値がPoint型の関数input Point input ( ){ Point p = {“noname”, 0, 0}; cin >> p.name << “, ” << p.x << “, ” << p.y << endl; return p; } 関数内で変更しても main関数(呼び出し元)の 変数には影響しない 構造体を用いることにより, return文で一度に複数の 情報を送ることができる
構造体の関数での利用(P.104)
Struct Point{ string name; int x, y; }; 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;
};
void update(Point& p){ p.x += 2;
p.y += 3; }
void print(const Point& p){
cout << p.name << “, ” << p.x << “, ” << p.y << endl; } 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++){ 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; } }void print(const Point *p){ if(p != 0){ cout << p -> name << “, ” << p -> x << “, ” << p -> y << endl; } } 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]); } 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」と出力せよ. // 実行例1Input a fruit: orange Alice, Tom
// 3人の名前と好きな果物リスト Alice : banana, apple, orange
Bob : apple, strawberry Tom : apple, orange
// 実行例2
Input a fruit: apple Alice, Bob, Tom
// 実行例3
Input a fruit: pineapple Not exist.