C言語

【C言語 構造体】構造体とは?構造体をわかりやすく解説!【初心者編】

c-language-structure.png

<この記事について>

  • 「構造体が苦手でよくわからない」、「構造体の仕組みがわかりにくい」などと悩んでいる方に向けて書きました。
  • この記事を読むことで、構造体が仕組みや使い方について理解することができます。
  • 結論としては、構造体は様々なデータ型を組み合わせ新しいデータ型を定義できます。構造体で配列を宣言したり、関数の戻り値・引数に構造体のデータ型も設定可能です。

こんにちは!こんばんは!

ちゃとらです(・ω・)/

C言語の学習で必ず出てくる構造体ですが、よくわからなくて苦手意識を持つ方は少なくはないと思います。

構造体は、JavaやC#などのオブジェクト指向でよく出てくるクラスの前進と言われているので、構造体を理解することはクラスの理解の手助けにもなります。

ちゃとら

構造体について分かりやすく解説していきます!

構造体とは

説明

構造体とは、様々なデータ型を組み合わせて、新しいデータ型として扱うもののことです。

例えば、スポーツ選手の名前や年齢、身長や体重などといった複数のデータを1つのデータとしてまとめることが可能です。

構造体のイメージ
/* 構造体の定義方法 */
struct 構造体タグ名
{
	データ型 メンバ変数名;
	データ型 メンバ変数名;
	…
};

/* 構造体変数の宣言方法 */
struct 構造体タグ名 変数名;
構造体の定義方法の解説
  • 構造体に属する1つ1つの要素のことをメンバと呼び、データ型を付けて定義する。
  • メンバのデータ型には、配列型やポインタ型のデータ型も使用可能。
  • メンバの宣言と構造体の{}の最後には;を付ける。
ちゃとら

余談ですが、構造体と配列は似て非なるものです。

構造体は様々なデータ型を扱うことができますが、配列は同一のデータ型しか扱うことができません。

サンプルコード

実際に、構造体を使ったプログラムで仕組みを説明していきます。

#include <stdio.h>
#include <string.h>

struct player
{
	char name[30];
	int age;
	double height;
	double weight;
};

int main(void)
{
	/* 構造体変数p1を宣言 */
	struct player p1;
	/* p1の各メンバ変数に値を代入 */
	strcpy_s(p1.name, 20, "Curry");
	p1.age = 34;
	p1.height = 188.0;
	p1.weight = 83.9;

	/* 構造体変数p2を宣言して初期化 */
	struct player p2 = { "Davis", 29, 213.4, 114.8 };

	printf("p1についての情報\n");
	printf("名前:%s 年齢:%d 身長:%.1fcm 体重:%.1fkg\n", p1.name, p1.age, p1.height, p1.weight);
	printf("p2についての情報\n");
	printf("名前:%s 年齢:%d 身長:%.1fcm 体重:%.1fkg\n", p2.name, p2.age, p2.height, p2.weight);

	return 0;
}
実行結果

p1についての情報
名前:Curry 年齢:34 身長:188.0cm 体重:83.9kg
p2についての情報
名前:Davis 年齢:29 身長:213.4cm 体重:114.8kg

構造体変数が宣言された際に、実物ができメモリ上に確保されます。

構造体playerには、char型の長さ30の配列メンバが1つ(30byte)とint型のメンバが1つ(4byte)、double型のメンバが2つ(16byte)なので、player型の変数が宣言されるたびに合計50byteの構造体データがメモリに保持されます。

メモリに保持される構造体
ちゃとら

ちなみに、int型はコンパイラによってバイト幅が変化しますが、大体は4byteです。

ドット演算子を使うことで、構造体の各メンバにアクセスすることができます。

17行目~20行目では、構造体変数p1宣言後にドット演算子を使って各メンバに値を代入しています。

また、23行目では、構造体変数p2宣言と同時に各メンバの値を代入することで初期化しています。

typedef宣言

typedef宣言とは、intやdoubleなどの既存のデータ型に対して、同意義の型名を与える役割があります。

typedef宣言を使って構造体を宣言することで、構造体変数を宣言する際に多少楽になります。

/* typedefを使った構造体の定義方法 */
typedef struct 
{
	データ型 メンバ変数名;
	データ型 メンバ変数名;
	…
}構造体タグ名;

/* 構造体変数の宣言方法 */
構造体タグ名 変数名;

typedef宣言を使って構造体を定義すれば、structを入れずに構造体変数を宣言することができます。

構造体の配列

構造体変数を配列型として宣言することも可能です。

もちろん、メモリ上には配列の要素数分の構造体が確保されます。

以下のプログラムで、実際に構造体の配列を使ってみました。

#include <stdio.h>

typedef struct
{
	char name[30];
	int age;
	double height;
	double weight;
}player;

int main(void)
{
	int i;
	/* 構造体の配列を宣言して初期化 */
	player pArray = { {"Curry",  34, 188.0,  83.9},
						 {"Davis",  29, 213.4, 114.8},
						 {"Butler", 33, 200.7, 104.3} };

	for (i = 0; i < 3; i++)
	{
		printf("player%dの情報\n", i+1);
		printf("名前:%s 年齢:%d 身長:%.1fcm 体重:%.1fkg\n", pArray[i].name, pArray[i].age, pArray[i].height, pArray[i].weight);
	}

	return 0;
}
実行結果

player1の情報
名前:Curry 年齢:34 身長:188.0cm 体重:83.9kg
player2の情報
名前:Davis 年齢:29 身長:213.4cm 体重:114.8kg
player3の情報
名前:Butler 年齢:33 身長:200.7cm 体重:104.3kg

このように、構造体の配列を宣言することで、いちいち構造体変数を宣言しなくても多くの構造体変数をまとめた配列を扱うことが可能です。

構造体と関数

関数の引数に構造体のデータ型を設定しておけば、構造体を渡すことが可能となります。

また、関数の戻り値を構造体のデータ型に設定すると構造体を返すことができます。

以下のプログラムで、実際に関数を使って構造体のやりとりをやってみました。

#include <stdio.h>

typedef struct
{
	int Japanese;
	int math;
	int English;
}SCORE;

SCORE scoreCreation()
{
	SCORE s = { 55, 98, 67 };
	return s;
}

void showScore(SCORE s)
{
	int sum = s.Japanese + s.math + s.English;
	printf("国語:%d 数学:%d 英語:%d\n", s.Japanese, s.math, s.English);
	printf("合計点:%d\n", sum);
}

int main(void)
{
	SCORE score = scoreCreation();
	showScore(score);

	return 0;
}
実行結果

国語:55 数学:98 英語:67
合計点:220

scoreCreation関数を呼び出すことで、構造体の実物を作成しその構造体変数を返してくれます。

showScore関数の引数に構造体変数を渡して呼び出すことで、構造体に登録されている各メンバ変数とそれらの合計値を表示してくれます。

このように、関数の戻り値・引数に構造体のデータ型を設定すると、関数内で構造体の表示や返却などが可能となります。

まとめ

構造体について

  1. 構造体は、様々なデータ型を組み合わせて新しいデータを定義できる。
  2. typedef宣言を使って構造体を定義することも可能。
  3. 構造体変数を配列として宣言することができ、関数の戻り値・引数に構造体のデータ型を設定することもできる。
ちゃとら

C言語において構造体は重要なので、しっかりと理解しておきましょう!

今回はここまでです。

ちゃとら(・ω・)/