ジャンル雑多なゲーム・ゲーム制作関連の色々な情報を取り扱っているブログ。最近はBlenderについてが中心。
[C, C++学習]C, C++言語再学習ノート-8日目- –構造体

[C, C++学習]C, C++言語再学習ノート-8日目- –構造体

構造体についての話 勉強しなおしの記録というか学習ノート-8日目-

Unreal Engin4すごい!(語彙力)ってなったので、少しいじりつつ、むかーしかじった事あるC,C++の学びなおしの学習ノート。この連載記事の詳しい趣旨と注意事項は1日目をご覧ください。

今回は構造体について書いていきたいと思います。

目次

構造体の基本について

構造体とは、複数の異なる型の変数を1つのまとまりとして扱う時に使う、型の一種のことです。

関連性のある情報の値が別々な型である場合に、それらをまとめて新しい型として作ってしまうわけです。

構造体テンプレートの定義

構造体テンプレートの定義の書式:
    struct タグ名 {
        型 メンバ名;
        型 メンバ名;
        型 メンバ名;
        型 メンバ名;
    };
struct column {
	int x; // X座標
	int y; // Y座標
	int z; // Z座標
	double radius; // 半径
	double height; // 高さ
	char* color; // 色
}

構造体につける名前を『構造体タグ名』といったり、構造体を構成している一つ一つの変数のことを『構造体のメンバ』と言ったりします。

普通、構造体の宣言は関数より先に行うようです。そうしておくことによって、プログラムで記述したすべての関数から構造体を扱うことが出来るという利点があります。

構造体を示すタグ名の前に“struct”キーワードをつけ、構造体で作る新しい型の定義をします。構造体の定義を、『構造体テンプレートの定義』と言います。

構造体変数の生成

型として定義したら、その型を使って変数を作ります。この、構造体から生成した変数を、『構造体変数』と言います。

構造体変数を生成する書式:
    struct タグ名 変数名;

ちなみに、構造体テンプレートの宣言と、構造体変数の生成を両方いっぺんにできたりします。↓

// 構造体テンプレートの定義
struct column {
	int x; // X座標
	int y; // Y座標
	int z; // Z座標
	double radius; // 半径
	double height; // 高さ
	char* color; // 色
}red_col;

複数の変数を生成する場合は、「, 」で続けて記述します。

// 構造体テンプレートの定義
struct column {
	int x; // X座標
	int y; // Y座標
	int z; // Z座標
	double radius; // 半径
	double height; // 高さ
	char* color; // 色
}red_col, blue_col;

メンバへの参照方法

構造体変数を生成した後、各構造体変数のメンバを参照するには、↓のようにします。

構造体変数のメンバを参照する書式:
    構造体変数名.メンバ

実際にやってみる

構造体テンプレートの定義、変数生成、メンバの値を参照して値を格納して、また参照して値を取り出す流れを実際にソースコードとして書いてみます。

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

// 定数
#define PI 3.14

// 構造体テンプレートの定義
struct column {
	int x; // X座標
	int y; // Y座標
	int z; // Z座標
	double radius; // 半径
	double height; // 高さ
	char* color; // 色
};

void main() {
	// struct 構造体タグ名 構造体変数名
	struct column red_col;
	red_col.x = 1;
	red_col.y = 2;
	red_col.z = 0;
	red_col.radius = 12.5;
	red_col.height = 30.0;
	red_col.color = "赤";

	printf("red_colは、x:%d, y:%d, z:%dの位置にある%s色の円柱で、\n", 
		red_col.x, red_col.y, red_col.z, red_col.color
	);
	printf("red_colは、半径%0.1lfcm, 高さ%0.1lfcmなので、体積は%0.4lf立方cmです。\n",
		red_col.radius, red_col.height, (red_col.radius * red_col.radius * PI)
	);
}

便利!typedef記憶クラス指定子の構造体での有効活用

過去の記事でちらっとご紹介したこの”typedef”ですが、改めましてご紹介します。これは、型に別名を付ける際に使うものです。構造体で定義した型の場合、”typedef”指定子は凄く便利に活用できます。

先ほども書きましたが構造体変数を生成する書式は、本来↓のようになりますね。

構造体変数を生成する書式:
struct タグ名 変数名;

この”struct”を省きたくなりませんか?(え、ならない?!それは、…失礼しました。)

“typedef”を使うと構造体変数の生成をする際に”struct”を省いて生成できるんです!

書式:typedef struct 構造体タグ名 新しく名付ける構造体の型名;

こうすることによって、”struct”を省いて、構造体変数を生成することが出来るようになります。先ほどの例から、該当箇所を抜粋して、改めて変数を生成してみますね。

// 省略して変数生成するための新しい型名を宣言
typedef struct column colm;
// 新しい型名で構造体変数を生成
colm  red_col;

ちなみに、これ、構造体テンプレートの宣言時に一緒くたに行ってしまえちゃいます。

// 構造体テンプレートの定義
typedef struct{ // 実はタグ名、省けます
	int x; // X座標
	int y; // Y座標
	int z; // Z座標
	double radius; // 半径
	double height; // 高さ
	char* color; // 色
} colm; // 型名

void main() {
	colm  red_col;
    /*
    略
    */
}

生成する変数が1個だとそれほど利点を感じられないかもしれませんが、複数になればずっとコーディングが楽になることが予想できるかと思います。

※とは言ったものの、実は”typedef”を使わないで”struct”を省いてしまっても、何の問題もなくコンパイルされてしまったりします。

C言語の拡張版であるC++では structをつけなくても構造体変数を宣言出来ます。 現在のコンパイラはほとんどがC++用なので、 structをつけなくても宣言出来てしまいます。

引用元:異なる型の変数をまとめる – 苦しんで覚えるC言語

構造体を関数の引数に渡す場合

構造体変数は、メンバごとにバラさなくても、そのまま関数の引数に渡せます。

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

#define PI 3.14

// 構造体テンプレートの定義
typedef struct{
	int x; // X座標
	int y; // Y座標
	int z; // Z座標
	double radius; // 半径
	double height; // 高さ
	char* color; // 色
}colm;

// プロトタイプ宣言
void column_calc(colm);

void main() {
	colm  red_col;
	red_col.x = 1;
	red_col.y = 2;
	red_col.z = 0;
	red_col.radius = 12.5;
	red_col.height = 30.0;
	red_col.color = "赤";

	column_calc(red_col);
}

// 構造体型colmの変数の情報を読み取り、体積を表示する関数
void column_calc(colm col) {
	printf("red_colは、x:%d, y:%d, z:%dの位置にある%s色の円柱で、\n",
		col.x, col.y, col.z, col.color
	);
	printf("red_colは、半径%0.1lfcm, 高さ%0.1lfcmなので、体積は%0.4lf立方cmです。\n",
		col.radius, col.height, (col.radius * col.radius * PI)
	);
}

※実はこれ、あまりお勧めできない渡し方なんです。構造体変数は複数の変数のまとまった変数なので、どうしてもメモリを大きくとってしまいます。そのため、 値渡しではあまり大きいデータを格納しているとすると、重くなってしまう可能性があります。

そこで、こういった関数の引数に渡す場合は、構造体をポインタにして、関数の引数には参照渡ししてしまう方が良いとされています。

構造体ポインタを使う際のメンバへの特殊な参照方法 –『アロー演算子”->”』

構造体でもポインタが使えたりしますが、メンバへの参照の仕方が少し特殊なやり方で、2種類あります。

書式:(*構造体変数名).メンバ
// プロトタイプ宣言
void column_calc(colm*);

void main() {
	colm  red_col;
	/*
        略
        */

	column_calc(&red_col);
}

void column_calc(colm *col) {
	printf("red_colは、x:%d, y:%d, z:%dの位置ある%s色の円柱で、\n",
		(*col).x, (*col).y, (*col).z, (*col).color
	);
	printf("red_colは、半径%0.1lfcm, 高さ%0.1lfcmなので、体積は%0.4lf立方cmです。\n",
		(*col).radius, (*col).height, ((*col).radius * (*col).radius * PI)
	);
}

↑の”()”で括った参照方法か、↓の「アロー演算子 “->” 」を使った書式。

書式:構造体変数名->メンバ
// プロトタイプ宣言
void column_calc(colm*);

void main() {
	colm  red_col;
	/*
        略
        */

	column_calc(&red_col);
}
void column_calc(colm *col) {
	printf("red_colは、x:%d, y:%d, z:%dの位置にある%s色の円柱で、\n",
		col->x, col->y, col->z, col->color
	);
	printf("red_colは、半径%0.1lfcm, 高さ%0.1lfcmなので、体積は%0.4lf立方cmです。\n",
		col->radius, col->height, (col->radius * col->radius * PI)
	);
}

これはお好みにもよりますが、どちらかと言えばコーディングが楽な『->』を使う方をお勧めしますよ。

応用:構造体変数の配列の作り方と各変数のメンバへの参照方法

過去記事でご紹介したポインタのポインタと、この構造体を組み合わせて、[]をつけない配列名はそもそも先頭のアドレスを格納している…いわばポインタ…二次配列の場合は、このポインタのポインタを使うのが便利…というところから、勘のいい方はお分かりになるのではないでしょうか…実は、構造体変数の配列で、めちゃくちゃ楽な参照方法があるんです!

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

#define PI 3.14

// 構造体テンプレートの定義
typedef struct{ 
	int x; // X座標
	int y; // Y座標
	int z; // Z座標
	double radius; // 半径
	double height; // 高さ
	char* color; // 色
}colm;

// プロトタイプ宣言
void column_calc(colm*);

void main() {
	colm colms[5] = {
		{1,2,0,12.5,30.0,"赤"},
		{2,4,10,8.0,10.0,"碧"},
		{5,3,8,13.3,5.0,"黄"},
		{0,9,100,4.5,3.0,"緑"},
		{11,2,20,8.5,11.0,"白"}
	};
	
	column_calc(colms);
}

void column_calc(colm *col) {
	for (int i = 0; i < 5; i++) {
		printf("%d番目の円柱は、x:%d, y:%d, z:%dの位置にある%s色の円柱で、\n",
			i,col[i].x, col[i].y, col[i].z, col[i].color
		);
		printf("半径%0.1lfcm, 高さ%0.1lfcmなので、体積は%0.4lf立方cmです。\n",
			col[i].radius, col[i].height, (col[i].radius * col[i].radius * PI)
		);
	}
}

あとがき

ご意見・ご感想・ご質問、また、ここ間違ってるよとか、もっといい方法あるよといったご指摘などございましたら、お手数ですがコメント欄やtwitterアカウントほろほろり(@_horo_horori)へお願いしますm(_ _)m

参考にさせていただいたページ・サイト一覧

Pocket

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください