cp-13. 構造体
C プログラミング入門)
URL: https://www.kkaneko.jp/pro/adp/index.html
1
金子邦彦
内容
例題1.住所録
例題2.構造体と関
構造体とは.構造体を扱関数
例題3.構造体のリスト
構造体とポインタの組み合わせ
2
目標
自分で「構造体」を定義する
自分で定義した構造体について,配列やポインタ
を作成する
3
データ型
基本データ型
char 文字(1文字)
int 整数
double 浮動小数
など
・その他のデータ
配列データの並び(文字列も,文字の並び)
ポインタ モリアドレス
構造体 いくつかのデータを「グループ化」したも
など
4
データの集まり
住所
氏名
支店名
口座番号
残高
銀行口座
氏名
年齢
住所
住所録
実数部
虚数部
複素数
5
構造体とは?
いくつかのデータを,グループ化して,新しい
型の名前を付けたも
基本データ型(整数,浮動小数)などの組み合
わせ
6
例題1.住所録
住所録を表示するプログラムを作る
住所録データを扱ために,構造体の配列を使
住所録は,名前(サイズ20の文字の配列),年齢,
住所(サイズ40の文字の配列)から構成する
7
#include <stdio.h>
#pragma warning(disable:4996)
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
の宣言と初期化
構造体のメンバの
読み出し 8
住所録
実行結果の例
name=Ken, age=20, address=NewYork
name=Bill, age=32, address=HongKong
name=Mike, age=35, address=Paris
9
プログラムとデータ
メモリ
a[0]
a[i].name
a[i].age
a[i].address
構造体の配列から
の値の読み出し
Ken 20 NewYork
Bill 32 HongKong
Mike 35 Paris
a[1]
a[2]
name age address
10
構造体の型宣言
構造体は,いくつかのデータをグループ化したも
の.
構造体には名前がある
それぞれのデータ(メンバとい)は,名前
(データの種類のこと)がある.
struct Person {
char name[20];
int age;
char address[40];
};
名前
メンバ
11
構造体メンバの読み書き
配列の中身を読み書きするときには,構造体
メンバを書く
例) a[i].name
これがメンバ
a[0] Ken 20 NewYork
Bill 32 HongKong
Mike 35 Paris
a[1]
a[2]
name age address
12
構造体の使い方
#include <stdio.h>
#pragma warning(disable:4996)
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 を宣
(変数の宣言)
ここで「構造体 Person 」を宣言
(構造体の宣言)
ここで a を使
13
例題2.構造体と関数
3人分の住所録を読み込んで,構造体の配列に格
納した後に,表示するプログラムを作る
住所録データを扱ために,造体の配列を使
住所録は,例題1と同じく,名前(サイズ20の文字
の配列),年齢,住所(サイズ40の文字の配列)か
ら構成する
ここでは,練習のため,1人分の住所録を読み込む関
数,1人分の住所録を表示する関数を作る.これら関
数への引数として,構造体のポインタを渡すこと.
14
#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 );
return;
}
構造体 Person の型宣言
構造体の
ポインタ渡しに関係する
15
int main()
{
struct Person a[3];
int i;
for ( i = 0; i < 3; i ++ ) {
read_person( &a[i] );
}
for ( i = 0; i < 3; i ++ ) {
print_person( &a[i] );
}
return 0;
}
構造体の
ポインタ渡しに関係する
16
構造体と関数
実行結果の例
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 17
関数呼び出しの流れ
read_person関数
void read_person( struct Person* a )
main 関数
int main()
read_person( &a[i] );
関数呼び出し
return;
戻り
print_person( &a[i] );
関数呼び出し
return;
戻り
print_person関数
void print_person( struct Person* a )
18
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 );
return;
}
int main()
{struct Person a[3];
int i;
for ( i = 0; i < 3; i ++ ) {
read_person( &a[i] );
}
for ( i = 0; i < 3; i ++ ) {
print_person( &a[i] );
}
return 0;
}
プログラム実行順
read_person関数
main関数
関数呼び出し
戻り
main 関数の先頭行
がプログラムの始まり
main 関数内の return
がプログラムの終わり
print_person関数
戻り
関数呼び出し
19
データの流れ
仮引数
メモリアドレスを,
read_person 関数に渡す
メモリアドレスを受け取って,
a」とい名前で使
main 関数には,
何も返さない
read_person関数
void read_person( struct Person* a )
main 関数
int main()
read_person( &a[i] );
関数呼び出し
return;
戻り
print_person( &a[i] );
関数呼び出し
return;
戻り
print_person関数
void print_person( struct Person* a )
main 関数には,
何も返さない
仮引数
メモリアドレスを受け取って,
a」とい名前で使
メモリアドレスを,
print_person 関数に渡す
20
read_person関数呼び出しでのデータの流
main 関数内で宣言された a
aメモリアドレス
main 関数内で宣言された a
と,仮引数で宣言された a
別のもの
仮引数
メモリアドレスを,
read_person 数に渡す
メモリアドレスを受け取って,
a」とい名前で使
read_person関数
void read_person( struct Person* a )
main 関数
int main()
read_person( &a[i] );
関数呼び出し
return;
戻り main 関数には,
何も返さない
a[0] Ken 20 NewYork
Bill 32 HongKong
Mike 35 Paris
a[1]
a[2]
name age address
a[0] Ken 20 NewYork
Bill 32 HongKong
Mike 35 Paris
a[1]
a[2]
name age address
21
関数への構造体の受け渡し
呼び出し側
「&」を付けて,メモリアドレスを,関数に渡す
例) read_person( &a[i] );
print_person( &a[i] );
関数側
メモリアドレスを受け取ることを宣言しておく
例) void read_person( struct Person* a )
void print_person( struct Person* a )
a[i] のメモリアドレス」とい
「メモリアドレスを受け取って,a
として使」とい意味 22
配列とポイン
プログラム例: 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 ならここ
23
演算子 -> の意味
メモリアドレスから,構造体メンバにアクセス
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」に入っているのはメモリアドレス 24
scanf a->age にだけ & をつける理
文字列
a->name, a->address は,「文字の配列の先頭メモリアド
レス」とい意味 (&は付けない)
整数,浮動小数
&a->age は,「整数データのメモリアドレス」とい意味
(&を忘れると,まく動かない)
scanf("%s", a->name );
scanf("%d", &a->age );
scanf("%s", a->address );
25
課題1.住所録の条件検索
3人分の住所録を読み込んで,構造体の配列に格納し
た後に,「20歳以上」のデータだを選んで表示す
るプログラムを作りなさい
ここでは,「20歳以上のデータだけを表示する機能」を
持った関数(main 関数とは別の関)を作るこ
住所録データを扱ために,造体の配列を使こと
住所録は,例題1と同じく,名前(サイズ20の文字の配
列),年齢,住所(サイズ40の文字の配列)から構成す
確かに,20歳以上のデータだけが表示されることを確認
すること
26
課題2.日付
日付を扱構造体を設計し、それを使ったプロ
グラムを作成しなさい
日付データを扱ために,構造体を使こと
日付は,年(整数データ),月(整数データ),日
(整数データ)から構成する
日付を読み込んで,1か月分のカレンダーを表示する
なプログラムであること.
例)日付が「2001年12月21日」なら,20
01年12月の1か月分のカレンダーを表示する
27
例題3.構造体のリスト
住所録の読み込みと表示を行プログラムを
作る
住所録データを扱ために,構造体のリストを使
住所録は,名前(サイズ20の文字の配列),年
齢,住所(サイズ40の文字の配列)から構成す
メニュー機能を作るために switch を使
28
#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 の型宣言
29
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 );
return;
}
構造体へのポインタ x
の宣言と初期化
構造体のメンバの
読み出し
30
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 ) {
return;
}
current = a->top;
do {
print_person( &(current->person) );
current = current->next;
} while( current != NULL );
}
構造体へのポインタ current
の宣言
構造体のメンバの
読み出し
構造体のメンバの
読み出し
31
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 );
break;
default:
printf("invalid command\n");
}
}
return;
}
構造体へのポインタ a
の宣言と初期化
switch 文については後述
32
int main()
{
menu();
return 0;
}
33
実行結果の例
menu
--------
1. input
2. print
9. exit
1
name=Ken
age=20
address=NewYork
menu
--------
1. input
2. print
9. exit
1
name=Bill
age=32
address=HongKong
menu
--------
1. input
2. print
9. exit
2
name=Bill, age=32, address=HongKong
name=Ken, age=20, address=NewYork 34
リスト
「何か」を順に並べたもの
順序に意味がある
ポインタを使ったリストの実現例
ポインタ
部分
データ
部分
1つの構造体
データ データ データ データ
NULL
NULLで,リストの
末端であることを表す
35
リストの例
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 PersonList
構造体
struct Person
構造体
struct Person
構造体
struct PersonNode
構造体
struct PersonNode
36
リストの辿り
current = a->top;
do { print_person( &(current->person) );
current = current->next;
} while( current != NULL );
Bill 32 HongKong Ken 20 NewYork
ポインタ
ポインタ NULL
ループ1回目は,cuurent は,
ここへのポインタ
ループ2回目は,cuurent は,
ここへのポインタ
( current->next NULL なので,
ループが終了する)
37
動的メモリ管
プログラムの実行中に、必要に応じて「メモ
リを確保」、「メモリを解放」すること
new, delete を使用
38
リストと動的メモリ管理
「新しいノード」を作るには,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;
}
39
挿入
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
の時点
40
動的メモリ管理のメリット
必要な分だけのメモリを、好きなときに得られ
1. 「リスト」は,必要に応じて,大きくなったり小さ
くなったりする
2. 配列は,あらかじめサイズが決まっていて,サイズ
を超えるデータは入らない
41
課題3.住所
例題3のプログラムについて,住所録の表
示を関数の再帰呼び出しによって行
に書き換えなさい.
42
課題3のヒント
表示関数
関数呼び出し
表示関数
表示関数
return;
関数呼び出し
return;
要素の表示
要素の表示
要素の表示
1番目の要素の表示
2番目の要素の表示
末尾の要素の表示
43