構造体 構造体, 構造体とポインタの組み合わせ,.

Slides:



Advertisements
Similar presentations
オブジェクト指向言語・ オブジェクト指向言語演習 中間試験回答例. Jan. 12, 2005 情報処理技術基礎演習 II 2 オブジェクト指向言語 中間試験解説 1  (1) 円柱の体積(円柱の体積 = 底面の円の面積 x 高さ) を求めるプログラムを作成しなさい。ただし、出力結果は、入 力した底面の円の半径.
Advertisements

情報基礎演習B 後半第5回 担当 岩村 TA 谷本君.
数理情報工学演習第一C プログラミング演習 (第3回 ) 2014/04/21
プログラミング入門2 第10回 動的な領域確保 情報工学科 篠埜 功.
第13回構造体.
課題解説: 関数の引数にポインタを使って2数を入れ替える
第12回構造体.
プログラミング入門2 第10回 構造体 情報工学科 篠埜 功.
プログラミング入門2 第10回 構造体 情報工学科 篠埜 功.
基礎プログラミングおよび演習 第9回
第13回 プログラミングⅡ 第13回
C言語講座 第4回 ポインタ.
第8回 プログラミングⅡ 第8回
構造体.
関数 関数とスタック.
第3回 配列,構造体,ポインタ ~ データ構造について学ぶための基礎~
第10回 プログラミングⅡ 第10回
C言語講座 第3回 ポインタ、配列.
ちょっとした練習問題① 配列iroを['R', 'W', 'R', 'R', 'W' , 'W' , 'W']を宣言して、「W」のときの配列の番号をprintfで表示するようなプログラムを記述しなさい。
プログラミング2 関数
Cプログラミング演習 中間まとめ2.
Cプログラミング演習 第6回 ファイル処理と配列.
繰り返し計算 while文, for文.
Cプログラミング演習.
第11回 宿題 出題日:12月21日 締切日:1月7日(木).
Cプログラミング演習 第7回 メモリ内でのデータの配置.
精密工学科プログラミング基礎 第10回資料 (12/18実施)
プログラミング 4 記憶の割り付け.
2005年度 データ構造とアルゴリズム 第3回 「C言語の復習:再帰的データ構造」
第10章 これはかなり大変な事項!! ~ポインタ~
プログラミング入門2 第11回 情報工学科 篠埜 功.
プログラミング入門2 第11回 情報工学科 篠埜 功.
第7回 プログラミングⅡ 第7回
岩村雅一 知能情報工学演習I 第10回(後半第4回) 岩村雅一
復習 前回の関数のまとめ(1) 関数はmain()関数または他の関数から呼び出されて実行される.
地域情報学 C言語プログラミング 第5回 ポインタ、関数、ファイル入出力 2017年11月17日
第11回 プログラミングⅡ 第11回
Cの実行モデル.
プログラミング基礎B 文字列の扱い.
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
整数データと浮動小数データ.
プログラミング入門2 第10回 構造体 情報工学科 篠埜 功.
疑似乱数, モンテカルロ法によるシミュレーション
メモリとメモリアドレス, ポインタ変数,関数へのポインタ渡し
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
プログラミング言語論 第六回 理工学部 情報システム工学科 新田直也.
精密工学科プログラミング基礎Ⅱ 第5回資料 今回の授業で習得してほしいこと: 構造体 (教科書 91 ページ)
プログラミング入門2 第9回 ポインタ 情報工学科 篠埜 功.
情報基礎演習B 後半第2回 担当 岩村 TA 谷本君.
ファイルの読み込み, ファイルからのデータの取り出し, ファイルの書き出し
地域情報学 C言語プログラミング 第4回 while文、do~while文、switch文、 2次元配列、ポインタ 2017年11月10日
ネットワーク・プログラミング Cプログラミングの基礎.
第5回 プログラミングⅡ 第5回
高度プログラミング演習 (11).
cp-15. 疑似乱数とシミュレーション (C プログラミング演習,Visual Studio 2019 対応)
cp-3. 計算 (C プログラミング演習,Visual Studio 2019 対応)
モジュール分割.
プログラミング 4 文字列.
プログラミング入門2 第6回 関数 情報工学科 篠埜 功.
岩村雅一 知能情報工学演習I 第10回(後半第4回) 岩村雅一
モバイルプログラミング第2回 C言語の基礎 (1).
第3回簡単なデータの入出力.
四則演算,変数 入力文,出力文,代入文, ライブラリ関数
プログラミング演習II 2004年11月 16日(第5回) 理学部数学科・木村巌.
プログラミング入門2 第5回 配列 変数宣言、初期化について
TList リスト構造とは? 複数のデータを扱うために、 データの内容と、次のデータへのポインタを持つ構造体を使う。
第2章 数値の入力と変数 scanfと変数をやります.
C言語講座第5回 2017 構造体.
第1章 文字の表示と計算 printfと演算子をやります 第1章 文字の表示と計算.
高度プログラミング演習 (10).
Presentation transcript:

構造体 構造体, 構造体とポインタの組み合わせ,

今日の内容 例題1.住所録 例題2.構造体と関数 構造体とは.構造体を扱う関数. 例題3.構造体のリスト 構造体とポインタの組み合わせ

今日の到達目標 自分で「構造体」を定義する 自分で定義した構造体について,配列やポインタを作成する

データ型 基本データ型 char 文字(1文字) int 整数 double 浮動小数 など ・その他のデータ型 配列 → データの並び(文字列も,文字の並び) ポインタ → メモリアドレス 構造体  → いくつかのデータを「グループ化」したもの

データの集まり 住所 氏名 支店名 口座番号 残高 氏名 年齢 住所 実数部 虚数部 銀行口座 住所録 複素数

構造体とは? いくつかのデータを,グループ化して,新しい型の名前を付けたもの 基本データ型(整数,浮動小数)などの組み合わせ

例題1.住所録 住所録を表示するプログラムを作る 住所録データを扱うために,構造体の配列を使う 住所録は,名前(サイズ20の文字の配列),年齢,住所(サイズ40の文字の配列)から構成する

構造体 Person の型宣言 構造体の配列 a の宣言と初期化 構造体のメンバの 読み出し #include <stdio.h> struct Person { char name[20]; int age; char address[40]; }; int main() { struct Person a[] = {{"Ken", 20, "NewYork"}, {"Bill", 32, "HongKong"}, {"Mike", 35, "Paris" }}; int i; for ( i = 0; i < 3; i ++ ) { printf( "name=%s, age=%d, address=%s\n", a[i].name, a[i].age, a[i].address ); } return 0; 構造体 Person の型宣言 構造体の配列 a の宣言と初期化 構造体のメンバの 読み出し

住所録 name=Ken, age=20, address=NewYork 実行結果の例 name=Ken, age=20, address=NewYork name=Bill, age=32, address=HongKong name=Mike, age=35, address=Paris

プログラムとデータ メモリ a[i].name a[i].age a[i].address 構造体の配列から の値の読み出し name Ken 20 NewYork a[1] Bill 32 HongKong a[2] Mike 35 Paris a[i].name a[i].age a[i].address 構造体の配列から の値の読み出し

構造体の型宣言 構造体は,いくつかのデータをグループ化したもの. 構造体には,名前がある それぞれのデータ(メンバという)は,名前と型(データの種類のこと)がある. struct Person { char name[20]; int age; char address[40]; }; 名前 メンバ

構造体メンバの読み書き 配列の中身を読み書きするときには,構造体のメンバを書く 例) a[i].name これがメンバ a[0] Ken 20 NewYork Bill 32 HongKong Mike 35 Paris a[1] a[2] name age address 配列の中身を読み書きするときには,構造体のメンバを書く 例) a[i].name これがメンバ

構造体の使い方 ①ここで「構造体 Person 」を宣言 (構造体の宣言) ②ここで Person を使って、「構造体の配列 a」 を宣言 #include <stdio.h> struct Person { char name[20]; int age; char address[40]; }; int main() { struct Person a[] = {{"Ken", 20, "NewYork"}, {"Bill", 32, "HongKong"}, {"Mike", 35, "Paris" }}; int i; for ( i = 0; i < 3; i ++ ) { printf( "name=%s, age=%d, address=%s\n", a[i].name, a[i].age, a[i].address ); } return 0; ①ここで「構造体 Person 」を宣言   (構造体の宣言) ②ここで Person を使って、「構造体の配列 a」 を宣言   (変数の宣言) ③ここで a を使う

例題2.構造体と関数 3人分の住所録を読み込んで,構造体の配列に格納した後に,表示するプログラムを作る 住所録データを扱うために,構造体の配列を使う 住所録は,例題1と同じく,名前(サイズ20の文字の配列),年齢,住所(サイズ40の文字の配列)から構成する ここでは,練習のため,1人分の住所録を読み込む関数,1人分の住所録を表示する関数を作る.これら関数への引数として,構造体のポインタを渡すこと.

構造体 Person の型宣言 構造体の ポインタ渡しに関係する #include <stdio.h> struct Person { char name[20]; int age; char address[40]; }; void read_person( struct Person* a ) { printf("name="); scanf("%s", a->name ); printf("age="); scanf("%d", &a->age ); printf("address="); scanf("%s", a->address ); return; } void print_person( struct Person* a ) printf( "name=%s, age=%d, address=%s\n", a->name, a->age, a->address ); 構造体 Person の型宣言 構造体の ポインタ渡しに関係する

構造体の ポインタ渡しに関係する int main() { struct Person a[3]; int i; for ( i = 0; i < 3; i ++ ) { read_person( &a[i] ); } print_person( &a[i] ); return 0; 構造体の ポインタ渡しに関係する

構造体と関数 実行結果の例 name=Ken age=20 address=NewYork name=Bill age=32 address=HongKong name=Mike age=35 address=Paris name=Ken, age=20, address=NewYork name=Bill, age=32, address=HongKong name=Mike, age=35, address=Paris

関数呼び出しの流れ main 関数 read_person関数 int main() read_person( &a[i] ); void read_person( struct Person* a ) 関数呼び出し read_person( &a[i] ); 戻り return; 関数呼び出し print_person( &a[i] ); print_person関数 void print_person( struct Person* a ) 戻り return;

プログラム実行順 read_person関数 print_person関数 main関数 ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑫ ⑬ void read_person( struct Person* a ) { printf("name="); scanf("%s", a->name ); printf("age="); scanf("%d", &a->age ); printf("address="); scanf("%s", a->address ); return; } void print_person( struct Person* a ) printf( "name=%s, age=%d, address=%s\n", a->name, a->age, a->address ); int main() struct Person a[3]; int i; for ( i = 0; i < 3; i ++ ) { read_person( &a[i] ); print_person( &a[i] ); return 0; ③ ④ read_person関数 ⑤ ⑥ ⑦ ⑧ ⑨ 戻り print_person関数 ⑫ ⑬ 戻り main 関数の先頭行 がプログラムの始まり ① ② 関数呼び出し main関数 ⑩ ⑪ 関数呼び出し main 関数内の return がプログラムの終わり ⑭

データの流れ 型 仮引数 型 仮引数 main 関数 read_person関数 int main() void read_person( struct Person* a ) 型 仮引数 関数呼び出し ②メモリアドレスを受け取って, 「a」という名前で使う read_person( &a[i] ); 戻り ③main 関数には, 何も返さない ① メモリアドレスを, read_person 関数に渡す return; 関数呼び出し print_person( &a[i] ); print_person関数 void print_person( struct Person* a ) ④ メモリアドレスを, print_person 関数に渡す 型 仮引数 ⑤メモリアドレスを受け取って, 「a」という名前で使う 戻り ⑥main 関数には, 何も返さない return;

read_person関数呼び出しでのデータの流れ name age address メモリアドレス a[0] Ken 20 NewYork name age address a[1] Bill 32 HongKong a[0] Ken 20 NewYork a[2] Mike 35 Paris a[1] Bill 32 HongKong main 関数内で宣言された a a[2] Mike 35 Paris main 関数内で宣言された a と,仮引数で宣言された a は 別のもの main 関数 int main() read_person関数 void read_person( struct Person* a ) 関数呼び出し 型 仮引数 ②メモリアドレスを受け取って, 「a」という名前で使う read_person( &a[i] ); ① メモリアドレスを, read_person 関数に渡す 戻り ③main 関数には, 何も返さない return;

関数への構造体の受け渡し 呼び出し側 関数側 「&」を付けて,メモリアドレスを,関数に渡す 例) read_person( &a[i] ); print_person( &a[i] ); 関数側 メモリアドレスを受け取ることを宣言しておく 例) void read_person( struct Person* a ) void print_person( struct Person* a ) 「a[i] のメモリアドレス」という意味 「メモリアドレスを受け取って,a として使う」という意味

配列とポインタ プログラム例: read_person( &a[i] ); a[0] Ken 20 NewYork Bill 32 HongKong Mike 35 Paris a[1] a[2] name age address i = 0 ならここ i = 1 ならここ i = 2 ならここ

演算子 -> の意味 メモリアドレスから,構造体メンバにアクセス 例) void read_person( struct Person* a ) { printf("name="); scanf("%s", a->name ); printf("age="); scanf("%d", &a->age ); printf("address="); scanf("%s", a->address ); return; } ここでは,「a」に入っているのはメモリアドレス

scanf で a->age にだけ & をつける理由 scanf("%s", a->name ); scanf("%d", &a->age ); scanf("%s", a->address ); 文字列 a->name, a->address は,「文字の配列の先頭メモリアドレス」という意味 (&は付けない) 整数,浮動小数 &a->age は,「整数データのメモリアドレス」という意味 (&を忘れると,うまく動かない)

課題1.住所録の条件検索 3人分の住所録を読み込んで,構造体の配列に格納した後に,「20歳以上」のデータだけを選んで表示するプログラムを作りなさい ここでは,「20歳以上のデータだけを表示する機能」を持った関数(main 関数とは別の関数)を作ること 住所録データを扱うために,構造体の配列を使うこと 住所録は,例題1と同じく,名前(サイズ20の文字の配列),年齢,住所(サイズ40の文字の配列)から構成する 確かに,20歳以上のデータだけが表示されることを確認すること

課題2.日付 日付を扱う構造体を設計し、それを使ったプログラムを作成しなさい 日付データを扱うために,構造体を使うこと 日付は,年(整数データ),月(整数データ),日(整数データ)から構成する 日付を読み込んで,1か月分のカレンダーを表示するようなプログラムであること. 例)日付が「2001年12月21日」なら,2001年12月の1か月分のカレンダーを表示する

例題3.構造体のリスト 住所録の読み込みと表示を行うプログラムを作る 住所録データを扱うために,構造体のリストを使う 住所録は,名前(サイズ20の文字の配列),年齢,住所(サイズ40の文字の配列)から構成する メニュー機能を作るために switch 文を使う

構造体 Person の型宣言 構造体 PersonNode の型宣言 構造体 PersonList の型宣言 #include <stdio.h> #include <string.h> #include <malloc.h> struct Person { char name[20]; int age; char address[40]; }; struct PersonNode { struct Person person; struct PersonNode* next; struct PersonList { struct PersonNode* top; 構造体 Person の型宣言 構造体 PersonNode の型宣言 構造体 PersonList の型宣言

構造体へのポインタ x の宣言と初期化 構造体のメンバの 読み出し void insert_head( struct PersonList* a, char* name, int age, char* address ) { struct PersonNode* x = new struct PersonNode(); strcpy(x->person.name, name); x->person.age = age; strcpy(x->person.address, address); x->next = a->top; a->top = x; return; } void input_data( struct PersonList* a ) char name[20]; int age; char address[40]; printf("name="); scanf("%s", name ); printf("age="); scanf("%d", &age ); printf("address="); scanf("%s", address ); insert_head( a, name, age, address ); 構造体へのポインタ x の宣言と初期化 構造体のメンバの 読み出し

構造体のメンバの 読み出し 構造体へのポインタ current の宣言 構造体のメンバの 読み出し void print_person( struct Person* a ) { printf( "name=%s, age=%d, address=%s\n", a->name, a->age, a->address ); return; } void print_data( struct PersonList* a ) PersonNode* current; if ( a->top == NULL ) { current = a->top; do { print_person( &(current->person) ); current = current->next; } while( current != NULL ); 構造体へのポインタ current の宣言 構造体のメンバの 読み出し

構造体へのポインタ a の宣言と初期化 switch 文については後述 void menu() { struct PersonList* a = new PersonList(); a->top = NULL; int command; while(1) { printf("menu\n"); printf("--------\n"); printf("1. input\n"); printf("2. print\n"); printf("9. exit\n"); scanf("%d", &command ); if ( command == 9 ) { return; } switch( command ) { case 1: input_data( a ); break; case 2: print_data( a ); default: printf("invalid command\n"); 構造体へのポインタ a の宣言と初期化 switch 文については後述

int main() { menu(); return 0; }

実行結果の例 menu -------- 1. input 2. print 9. exit 1 name=Ken age=20 address=NewYork name=Bill age=32 address=HongKong 2 name=Bill, age=32, address=HongKong name=Ken, age=20, address=NewYork 実行結果の例

リスト 「何か」を順に並べたもの 順序に意味がある ポインタを使ったリストの実現例 NULL データ 部分 ポインタ 部分 末端であることを表す 1つの構造体

リストの例 構造体 struct PersonList struct Person { char name[20]; int age; char address[40]; }; struct PersonNode { struct Person person; struct PersonNode* next; struct PersonList { struct PersonNode* top; ポインタ Bill 32 HongKong ポインタ Ken 20 NewYork NULL 構造体 struct Person 構造体 struct Person 構造体 struct PersonNode 構造体 struct PersonNode

リストの辿り current = a->top; do { ポインタ Bill 32 HongKong ポインタ Ken 20 NewYork NULL ループ1回目は,cuurent は, ここへのポインタ ループ2回目は,cuurent は, ここへのポインタ ( current->next が NULL なので, ループが終了する) current = a->top; do { print_person( &(current->person) ); current = current->next; } while( current != NULL );

動的メモリ管理 プログラムの実行中に、必要に応じて「メモリを確保」、「メモリを解放」すること new, delete を使用

リストと動的メモリ管理 「新しいノード」を作るには,new あるいは malloc を 使う.下記は new を使った例 void insert_head( struct PersonList* a, char* name, int age, char* address ) { struct PersonNode* x = new struct PersonNode(); strcpy(x->person.name, name); x->person.age = age; strcpy(x->person.address, address); x->next = a->top; a->top = x; return; }

挿入 ポインタ ポインタ Bill 32 HongKong Ken 20 NewYork ポインタ NULL Bill 32 HongKong Ken 20 NewYork ポインタ NULL ⑤の時点 ⑥の時点 void insert_head( struct PersonList* a, char* name, int age, char* address ) { struct PersonNode* x = new struct PersonNode(); strcpy(x->person.name, name); x->person.age = age; strcpy(x->person.address, address); x->next = a->top; a->top = x; return; } ① ② ③ ④ ⑤ ⑥

動的メモリ管理のメリット 必要な分だけのメモリを、好きなときに得られる 「リスト」は,必要に応じて,大きくなったり小さくなったりする 配列は,あらかじめサイズが決まっていて,サイズを超えるデータは入らない

課題3.住所録 例題3のプログラムについて,住所録の表示を関数の再帰呼び出しによって行うように書き換えなさい.

課題3のヒント 表示関数 1番目の要素の表示 表示関数 2番目の要素の表示 表示関数 末尾の要素の表示 return; return; 関数呼び出し 要素の表示 表示関数 末尾の要素の表示 関数呼び出し 要素の表示 return; return;