第六回(構造体)


構造体

構造体とはchar型やint型、float型のような異なったデータ型をまとめて扱いたいときに使うものです。

構造体の型枠の宣言

型枠の宣言はどこに書いてもいいですが、一般的にはヘッダファイルやプリプロセッサの下からプロトタイプ宣言までの間に書くのが推奨されています。
また、構造体の中の変数をメンバ変数と呼びます。
書き方は以下の通りです。

struct 構造体の種類名{
	型名 メンバ変数;
};
です。 注意としては}で閉じた後に;をつけるところでしょうか。 実際の例で見せると、
構造体の型枠の例
struct Name{
	int i;
	char c;
	float f;
};
といった感じです。

構造体の宣言

構造体の宣言は普通のデータ型と似た感じで使います。書き方としてはstruct 構造体の種類の名前 この宣言でできる構造体の変数名といった感じです。
構造体の種類の名前でどの構造体の呼び出しかを示し、次のこの宣言でできる構造体の変数名で他のものと区別します。
上の例で例を示すと、struct Name 好きな変数名;です。

構造体の参照

構造体の参照は構造体の変数名.メンバ変数名で行えます。
.のことをドット演算子(メンバアクセス演算子)と呼びます。

ドット演算子

ドット演算子とは構造体のメンバへのアクセスをするために使う演算子です。そのためにメンバアクセス演算子とも呼びます。(C言語以外では別の意味があったりもしますが、初級C言語ゼミなので割愛させてもらいます。)

では、構造体を使った例をみてみましょう。
サンプルソース
#include<stdio.h>
#include<stdlib.h>

#define MAX 256

//構造体の型枠の宣言
struct Data{
	char name[MAX];
	int age;
};

int main(void);

int main(void){
	
	struct Data data;	//構造体の宣言
	char a[MAX];
	
	printf("名前 : ");
	fgets(data.name, MAX, stdin);
	printf("年齢 :");
	fgets(a, MAX, stdin);
	data.age = atoi(a);
	
	printf("名前: %s年齢: %d歳\n", data.name, data.age);
	
	return(0);
}
実行例
名前 : Tako
年齢 : 19
名前: Tako
年齢: 19歳

構造体同士の代入

構造体同士の代入は非常に単純で、構造体の変数名(代入する側) = 構造体の変数名(代入される側)で全て代入できます。 しかし、別の種類の構造体へは代入できません。同じ種類の構造体でなければなりません。

以下に例を示します。
サンプルソース
#include<stdio.h>
#include<stdlib.h>

#define MAX 256

struct Data{
	char name[MAX];
	int age;
};

int main(void);

int main(void){
	
	struct Data data, data2;
	char a[MAX];
	
	printf("名前 : ");
	fgets(data.name, MAX, stdin);
	printf("年齢 : ");
	fgets(a, MAX, stdin);
	data.age = atoi(a);
	
	data2 = data;	//構造体同士の代入
	
	printf("名前: %s年齢: %d歳\n", data2.name, data2.age);
	
	return(0);
}
実行例
名前 : しもぶ〜
年齢 : 0
名前: しもぶ〜
年齢: 0歳

構造体の配列

構造体は配列として宣言することもできます。

構造体の初期化

構造体の初期化は{0}のように行います。配列の数が1つの時は同じですが、メンバ変数が配列をもっているときは{{0}}のように書きます。

構造体と関数

構造体は関数の引数に渡すことも返り値として返すこともできます。

以下に例を示します。
サンプルソース
#include<stdio.h>
#include<stdlib.h>

#define MAX 256
#define NUM_MAX 4
#define B_MAX 10

struct Data{
	char name[MAX];
	char bt[B_MAX];
};

int main(void);
struct Data input_data(struct Data data);
void print_data(struct Data data[NUM_MAX]);

int main(void){
	
	struct Data data[NUM_MAX] = {{0}};	//構造体の配列と初期化
	int i;
	
	for(i = 0; i < NUM_MAX; i++){
		data[i] = input_data(data[i]);	//構造体の引数渡し
	}

	print_data(data);	//構造体すべてを引数から渡す
	return(0);
}

struct Data input_data(struct Data data){
	
	printf("Name?\n> ");
	fgets(data.name, MAX, stdin);
	printf("BloodType?\n> ");
	fgets(data.bt, B_MAX, stdin);
	
	return(data);
}

void print_data(struct Data data[NUM_MAX]){
	
	int i;
	
	for(i = 0; i < NUM_MAX; i++){
		printf("Name : %s", data[i].name);
		printf("BloodType : %s", data[i].bt);
	}
}
実行例
Name?
> しもぶ〜
BloodType?
> しも
Name?
> Sitiyan
BloodType?
> A
Name?
> Y・ハルヒ
BloodType?
> A
Name?
> RCC 太郎
BloodType?
> O
Name : しもぶ〜
BloodType : しも
Name : Sitiyan
BloodType : A
Name : Y・ハルヒ
BloodType : A
Name : RCC 太郎
BloodType : O

typedef

typedefとは既存の型に新しい名前をつけることのできるものです。プログラムを書くのを少し楽にしてくれます。
書き方はtypedef 既存の型名 新しい型名です。また、構造体の時は typedef struct 構造体の種類の名前{ メンバ }新しい型名;と書きます。

typedefの例
typedef int INTEGER;

こう書くことで、int i;とINTEGER i;が同じ意味になります。

また、
typedefの例
typedef char string;
こうすることで、"気持ちだけ" C言語でstring型が使えてる気分になれます。(string型とは他のプログラミング言語にある型です。
※実際は違うので、宣言後にstr = "RCC";とかやっても代入できません。)

そして、もっともtypedefを使うのが構造体です。では例です。

構造体でのtypedefの例
typedef struct PC{
	char PC_name[MAX];
	char mouse_name[MAX];
	char keybord_name[MAX];
	char printer_name[MAX];
	char scanner_name[MAX];
	float display_size;
	int usb_port;
}PC;
これで、PC pc[100];と宣言するだけで、struct PC pc[100];と宣言するのと同じになります。

列挙体

列挙体とは構造体は変数をメンバとしておきましたが、その反対で定数をメンバとしておきます。まずは列挙体の型枠の例を示します。

サンプルソース
enum{
	ZERO,
	ONE,
	TWO
};
これで、ZEROには0、ONEには1、TWOには2と入ります。また、
サンプルソース
enum{
	ZERO = 1,
	ONE,
	TWO
};
と書くと、ZEROには1、ONEには2、TWOには3が入ります。 また、enum 任意の名前{ 定数, };とすることも可能です。
サンプルソース
enum TOWN{
	FIRST_TOWN,
	NEXT_TOWN,
	LAST_TOWN
};
サンプルソース
#include<stdio.h>
#include<stdlib.h>

#define MAX 256

enum TOP{
	NEW,
	LOAD,
	QUIT
};

enum BOOL{
	FALSE,
	TRUE
};

int main(void);

int main(void){
	
	char select[MAX];
	int flag = FALSE;
	
	while(TRUE){
		printf("0:NEW GAME 1:LOAD GAME 2:QUIT\n");
		printf("select :");
		fgets(select, MAX, stdin);
		
		switch(atoi(select)){
			
			case NEW:
				printf("初めから始めます。\n\n");
				break;
				
			case LOAD:
				printf("データをロードします。\n\n");
				break;
				
			case QUIT:
				printf("終了します。\n");
				flag = TRUE;
				break;
		}
		
		if(flag == TRUE)break;
	}
	
	return(0);
}
実行例
0:NEW GAME 1:LOAD GAME 2:QUIT
select: 0
初めから始めます。

0:NEW GAME 1:LOAD GAME 2:QUIT
select: 1
データをロードします。

0:NEW GAME 1:LOAD GAME 2:QUIT
select: 2
終了します。
リダイレクション

リダイレクションとは、標準入力が必要なプログラムでどのように入力するか記述したファイルを使って、 自分で入力する大変さをなくすことができるもののことです。
例えば、fgets関数から3回標準入力するプログラム(○○.exe)があった場合、 a、b、cと入力する代わりに、ファイル(.txtや.c等)に以下のように記述するとします。

△△.txt
a
b
c

こう書かれたファイル(△△.txt)を実行するプログラム(○○.exe)で使う場合、コマンドプロンプト、またはCPadの上のコマンド欄に

○○.exe < △△.txt
と書くことで行うことができます。
これは、プロコン(プログラミングコンテスト)でも使う便利な機能なので、覚えておくといいです。

基本問題

現在のサークル員のデータを管理したいので管理プログラムを構造体と列挙体を用いて作成してください。(必要な項目:回生、本名、アカウント…etc)
このプログラムには以下の機能がある。

(また、ファイル操作は用いなくてよく、プログラムが終了するまで記録されるだけでいいものとする。)

応用問題

基本問題の管理プログラムに以下の機能を追加してください。

※削除後は削除されたデータ以外が問題なく表示されていなければならない。

次回予告

次回はポインタについて説明します。

戻る