---
title: cp-13. 構造体
tags:  #プログラミング #c #構造体 #リスト #ポインタ #ー＞ #動的メモリ管理 #金子邦彦研究室  
author: [kunihikokaneko](https://www.docswell.com/user/6674398749)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/DJYPN8QYJM.jpg?width=480
description: C プログラミング入門 （スライド資料とプログラム例）（Visual Studio 2019 を使用）（全１５回） https://www.kkaneko.jp/pro/adp/index.html  金子邦彦研究室ホームページ https://www.kkaneko.jp/index.html
published: February 21, 22
canonical: https://www.docswell.com/s/6674398749/Z6J2E5-2022-02-21-181440
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/DJYPN8QYJM.jpg)

cp-13. 構造体
（C プログラミング入門）
URL: https://www.kkaneko.jp/pro/adp/index.html
金子邦彦
1


# Page. 2

![Page Image](https://bcdn.docswell.com/page/V7N5XQKRE8.jpg)

内容
例題１．住所録
例題２．構造体と関数
構造体とは．構造体を扱う関数．
例題３．構造体のリスト
• 構造体とポインタの組み合わせ
2


# Page. 3

![Page Image](https://bcdn.docswell.com/page/YJ9Y8DVZ73.jpg)

目標
• 自分で「構造体」を定義する
• 自分で定義した構造体について，配列やポインタ
を作成する
3


# Page. 4

![Page Image](https://bcdn.docswell.com/page/GJ86834YED.jpg)

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


# Page. 5

![Page Image](https://bcdn.docswell.com/page/LJL9RV697R.jpg)

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


# Page. 6

![Page Image](https://bcdn.docswell.com/page/47MZRG5VJW.jpg)

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


# Page. 7

![Page Image](https://bcdn.docswell.com/page/P7R6RZ3WE9.jpg)

例題１．住所録
• 住所録を表示するプログラムを作る
• 住所録データを扱うために，構造体の配列を使う
• 住所録は，名前（サイズ２０の文字の配列），年齢，
住所（サイズ４０の文字の配列）から構成する
7


# Page. 8

![Page Image](https://bcdn.docswell.com/page/PJXMR94VEX.jpg)

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


# Page. 9

![Page Image](https://bcdn.docswell.com/page/3JKGRPZ57D.jpg)

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


# Page. 10

![Page Image](https://bcdn.docswell.com/page/LE3N8XD1E5.jpg)

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


# Page. 11

![Page Image](https://bcdn.docswell.com/page/8EDWM2PK7G.jpg)

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


# Page. 12

![Page Image](https://bcdn.docswell.com/page/V7P9R1N3J8.jpg)

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


# Page. 13

![Page Image](https://bcdn.docswell.com/page/2JVMRYKNJQ.jpg)

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


# Page. 14

![Page Image](https://bcdn.docswell.com/page/5EGGM9Q5EL.jpg)

例題２．構造体と関数
• ３人分の住所録を読み込んで，構造体の配列に格
納した後に，表示するプログラムを作る
• 住所録データを扱うために，構造体の配列を使う
• 住所録は，例題１と同じく，名前（サイズ２０の文字
の配列），年齢，住所（サイズ４０の文字の配列）か
ら構成する
• ここでは，練習のため，１人分の住所録を読み込む関
数，１人分の住所録を表示する関数を作る．これら関
数への引数として，構造体のポインタを渡すこと．
14


# Page. 15

![Page Image](https://bcdn.docswell.com/page/4JQXRWKLEP.jpg)

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


# Page. 16

![Page Image](https://bcdn.docswell.com/page/K7468KP5E1.jpg)

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


# Page. 17

![Page Image](https://bcdn.docswell.com/page/LJ1V2X92EG.jpg)

構造体と関数
実行結果の例
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


# Page. 18

![Page Image](https://bcdn.docswell.com/page/GJWLRNQ2E2.jpg)

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


# Page. 19

![Page Image](https://bcdn.docswell.com/page/4EZMR3W4J3.jpg)

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


# Page. 20

![Page Image](https://bcdn.docswell.com/page/Y76NRXGG7V.jpg)

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


# Page. 21

![Page Image](https://bcdn.docswell.com/page/G7558XVX74.jpg)

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


# Page. 22

![Page Image](https://bcdn.docswell.com/page/9J2NKX3QER.jpg)

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


# Page. 23

![Page Image](https://bcdn.docswell.com/page/DEYPN81YEM.jpg)

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


# Page. 24

![Page Image](https://bcdn.docswell.com/page/VJN5XQ2R78.jpg)

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


# Page. 25

![Page Image](https://bcdn.docswell.com/page/YE9Y8D1ZJ3.jpg)

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


# Page. 26

![Page Image](https://bcdn.docswell.com/page/GE86833Y7D.jpg)

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


# Page. 27

![Page Image](https://bcdn.docswell.com/page/LEL9RVV9JR.jpg)

課題２．日付
•
日付を扱う構造体を設計し、それを使ったプロ
グラムを作成しなさい
•
日付データを扱うために，構造体を使うこと
•
日付は，年（整数データ），月（整数データ），日
（整数データ）から構成する
•
日付を読み込んで，1か月分のカレンダーを表示する
ようなプログラムであること．
例）日付が「２００１年１２月２１日」なら，２０
０１年１２月の１か月分のカレンダーを表示する
27


# Page. 28

![Page Image](https://bcdn.docswell.com/page/4JMZRGGVEW.jpg)

例題３．構造体のリスト
• 住所録の読み込みと表示を行うプログラムを
作る
• 住所録データを扱うために，構造体のリストを使
う
• 住所録は，名前（サイズ２０の文字の配列），年
齢，住所（サイズ４０の文字の配列）から構成す
る
• メニュー機能を作るために switch 文を使う
28


# Page. 29

![Page Image](https://bcdn.docswell.com/page/PJR6RZZW79.jpg)

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


# Page. 30

![Page Image](https://bcdn.docswell.com/page/PEXMR99V7X.jpg)

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


# Page. 31

![Page Image](https://bcdn.docswell.com/page/3EKGRPP5JD.jpg)

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


# Page. 32

![Page Image](https://bcdn.docswell.com/page/L73N8XX175.jpg)

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


# Page. 33

![Page Image](https://bcdn.docswell.com/page/87DWM22KJG.jpg)

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


# Page. 34

![Page Image](https://bcdn.docswell.com/page/VJP9R113E8.jpg)

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


# Page. 35

![Page Image](https://bcdn.docswell.com/page/2EVMRYYNEQ.jpg)

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


# Page. 36

![Page Image](https://bcdn.docswell.com/page/57GGM9957L.jpg)

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


# Page. 37

![Page Image](https://bcdn.docswell.com/page/4EQXRWWL7P.jpg)

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


# Page. 38

![Page Image](https://bcdn.docswell.com/page/KJ468KK571.jpg)

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


# Page. 39

![Page Image](https://bcdn.docswell.com/page/LE1V2XX27G.jpg)

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


# Page. 40

![Page Image](https://bcdn.docswell.com/page/GEWLRNN272.jpg)

挿入
ポインタ
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-&gt;person.name, name);
③ x-&gt;person.age = age;
④ strcpy(x-&gt;person.address, address);
⑤ x-&gt;next = a-&gt;top;
⑥ a-&gt;top = x;
return;
}
40


# Page. 41

![Page Image](https://bcdn.docswell.com/page/47ZMR334E3.jpg)

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


# Page. 42

![Page Image](https://bcdn.docswell.com/page/YJ6NRXXGJV.jpg)

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


# Page. 43

![Page Image](https://bcdn.docswell.com/page/GJ558XXXJ4.jpg)

課題３のヒント
表示関数
１番目の要素の表示
要素の表示
関数呼び出し
表示関数
２番目の要素の表示
要素の表示
関数呼び出し
表示関数
末尾の要素の表示
要素の表示
return;
return;
43


