C言語の構造体、構造体ポインタの基本事項

作田 誠

目次

1  C言語の構造体、構造体ポインタの基本事項

C言語では、ひとまとまりのデータを 構造体 (structure) を使って表現する。

1.1 構造体の基本

構造体はキーワード struct を使って指定する。

(例)

人の名前、年齢、身長、体重、住所を扱う構造体 tagPerson の定義。

	struct tagPerson {
		char name[256];
		short age;
		float height;
		float weight;
		char address[256];
	};

struct の後の tagPerson の部分を構造体のタグ名 (tag name)と言う。
また、構造体の中の変数をメンバ変数 (member variable)と言う。

構造体 tagPerson 型の変数の定義。

	struct tagPerson  h1;

初期値を設定する場合は、波括弧 { } の中に宣言順にメンバ変数の初期値をコンマで区切って並べればよい。

	struct tagPerson  h2 = {"山田 太郎", 18, 180.0F, 70.5F, "不定"};

構造体 tagPerson 型の変数の利用

構造体のメンバには

構造体変数 . メンバ名

でアクセスできる。

	strcpy( h1.name, "福岡 工大" );
	h1.age = 22;

	printf( "名前: %s  年齢:%d  身長:%.1f  体重:%.1f  住所:%s\n",
		h2.name, h2.age, h2.height, h2.weight, h2.address );

1.2 構造体のポインタ

構造体型の変数のアドレスをポインタ変数として利用できる。
構造体のポインタからメンバにアクセスするには

構造体ポインタ -> メンバ名

または

(*構造体ポインタ) . メンバ名

という記法を使う。

※ 注意 ※

*構造体ポインタ . メンバ名

という括弧をつけない書き方は

*(構造体ポインタ . メンバ名)

と同等になるので、エラーになる。
※    ※

(例)

	struct tagPerson  *p;
	p = &h1;
	printf( "名前 %s  の年齢は %d\n",
		p->name, p->age );

1.3 型定義の利用

構造体をキーワード typedef を使って型定義すると、型名として使用できるようになる。

(例)

先の例の構造体 tagPersonを、構造体タグ名をつけずに、typedef によって Person という型として定義する。

	typedef struct {
		char name[256];
		short age;
		float height;
		float weight;
		char address[256];
	} Person;

この場合、Personという型で変数を指定できる。ポインタ変数も同様に利用できる。

	Person h3;
	Person *pp;

	strcpy( h3.name, "小川 遥" );
	strcpy( h3.address, "日本" );
	h3.age = 21;
	h3.height = 170.0F;
	h3.weight = 59.5F;

	pp = &h3;
	printf( "名前: %s  年齢:%d  身長:%.1f  体重:%.1f  住所:%s\n",
		pp->name, pp->age, pp->height, pp->weight, pp->address );

1.4 関数のパラメータとしての利用

構造体の変数は関数にそのままパラメータとして渡すことができる。ただし、その際コピーが作られるため大きなサイズの構造体ではメモリ効率が悪くなる場合がある。

また、アドレス演算子 & によってポインタを求めて関数に渡すこともできる。こちらの方が一般的な手法と言える。

(例)

先の Person 型のポインタをパラメータとして受け取る関数 getBMI

	double getBMI(const Person *p) {
		double hm = p->height / 100;
		return p->weight / (hm * hm);
	}

上の関数の呼び出し例

	double bmi = getBMI( &h3 );

ポインタ変数 pp を使って

	double bmi2 = getBMI( pp );

としてもよい。

1.5 配列とポインタの復習

構造体の配列は、intやdoubleの配列と同様に定義できる。型定義を使った場合も同様である。

	struct tagPerson ar1[10];  /* ar1[0] ~ ar1[9] までの10個の要素を持つ配列 */

	Person ar2[15];  /* ar2[0] ~ ar2[14] までの15個の要素を持つ配列 */

C言語では、配列とポインタに以下の関係が成立する。

配列名[インデックス] ≡ *(配列名 + インデックス) ≡ *(インデックス + 配列名) ≡ インデックス[配列名]

※ 例えば a を一次元配列としたとき、
a[3] ≡ *(a + 3) ≡ *(3 + a) ≡ 3[a]

アドレス演算子 & については、

&配列名[インデックス] ≡ 配列名 + インデックス

※ 例えば a を一次元配列としたとき、
&a[0] ≡ a
&a[4] ≡ a + 4

同じ配列の中の要素を指す2つのポインタ同士の減算によって、2つのポインタが指している要素のインデックスの差を求められる。(正確には、標準ヘッダ <stddef.h> で定義されている ptrdiff_t という符号付き整数型でインデックスの差が求められる)

	Person ar3[100];

	Person *pr1 = &ar3[5];  /*  ar3 + 5 と同じ  */
	Person *pr2 = ar3 + 12;  /*  &ar3[12] と同じ  */
	Person *pr3 = &ar3[23];  /*  ar3 + 23 と同じ  */

	‥‥‥

	// C99 以降の書き方
	printf( "%td\n", pr2 - pr1 );  /*  12 - 5 = 7 なので 7 を表示 */
	printf( "%td\n", pr2 - pr3 );  /*  12 - 23 = -11 なので -11 を表示 */
	/* C99 より前の書き方例 */
	printf( "%d\n", (int)(pr2 - pr1) );  /*  12 - 5 = 7 なので 7 を表示 */
	printf( "%d\n", (int)(pr2 - pr3) );  /*  12 - 23 = -11 なので -11 を表示 */