9-2 構造体の基本

この記事は約6分で読めます。

構造体の初期化

配列を初期化するときは中カッコ{}を使用しましたが、構造体も中カッコ{}を用いて初期化できます。

構造体型名 変数名={メンバ1,メンバ2,...};

ただし、初期化子は構造体の中でメンバを定義した順番で書かなかければなりません。

#include <stdio.h>
#include <string.h>
typedef struct tagperson{
	char name[20];
	int age;
}person;
int main(void) {
	person me={"山田太郎",20};
	printf("私の名前=%s\n私の年齢=%d\n",me.name,me.age);
	return 0;
}

実行結果

私の名前=山田太郎
私の年齢=20

この例では、構造体の中で定義したメンバはname,ageの順なので、初期化子もname,ageの順で指定する必要があります。

構造体のポインタとアロー演算子

普通の変数と同じように構造体変数を指すポインタを考えることができます。構造体全体へのポインタも、構造体のあるメンバへのポインタも取得できます。

#include <stdio.h>
#include <string.h>
typedef struct tagme{
	char name[20];
	int age;
}person;
int main(void) {
	person me={"山田太郎",20};
	printf("私の名前=%s\n私の年齢=%d\n",me.name,me.age);
	printf("アドレス:\n&me=%p\nme.name=%p\n&me.age=%p\n",&me,me.name,&me.age);
	return 0;
}

実行結果

私の名前=山田太郎
私の年齢=20
アドレス:
&me=0019FF24
me.name=0019FF24
&me.age=0019FF38
(実行結果は環境により異なります)

配列名は単体で先頭要素へのポインタを表すことに注意してください。ここでは、構造体全体のポインタの値は先頭要素であるメンバname[0]のアドレスと等しくなっています。アドレスは16進数なので、name[0]とageのアドレスは10進数でちょうど20=(sizeof char x20)だけ離れていることがわかります。プログラムから&meとme.nameの値が等しいことがわかりますが、型が違います。&meはpersonへのポインタ型であり、me.nameはcharへのポインタ型です。&より.のほうが演算子の優先順位が高いのでカッコをつける必要はありません。

もちろん、ポインタを引数に受け取ると関数から値を変更することができます。

#include <stdio.h>
#include <string.h>
typedef struct tagme{
	char name[20];
	int age;
}person;
void askabout(person *you){
	puts("名前を入力してください。");
	scanf("%s",(*you).name);
	puts("年齢は?");
	scanf("%d",&(*you).age);
}
int main(void) {
	person me;
	askabout(&me);
	printf("私の名前=%s\n私の年齢=%d\n",me.name,me.age);
	return 0;
}

実行結果

名前を入力してください。
gukos
年齢は?
19
私の名前=gukos
私の年齢=19

9行目について。nameが一番先に定義されたメンバなので、今回に限り構造体全体のポインタを指定しても構いません。

	scanf("%s",you);

11行目について。仮引数youはポインタなので、youが指す構造体変数のメンバageのポインタをscanfに渡す必要があります。メンバ演算子.が最も優先順位が高いので、()を使う必要があります。よって、このような書き方になります。

ポインタが指す構造体のメンバにアクセスする時

(*you).age

のようにカッコを使う必要がありますが、毎回この書き方をするのは面倒です。そんなときのために、アロー演算子が用意されています。アロー演算子はポインタが指す構造体のメンバにアクセスするための演算子です。アロー演算子を用いると次のように書けます。

you->age

さきほどのプログラム中のaskabout関数はこう書けます。

void askabout(person *you){
	puts("名前を入力してください。");
	scanf("%s",you->name);
	puts("年齢は?");
	scanf("%d",&you->age);
}

構造体のコピー

構造体のメンバは普通の変数と同様に扱え、同じ型の値や変数を代入できます。それに加えて、構造体変数動詞を代入できます。

#include <stdio.h>
#include <string.h>
typedef struct tagme{
	char name[20];
	int age;
}person;
void askabout(person *you){
	puts("名前を入力してください。");
	scanf("%s",you->name);
	puts("年齢は?");
	scanf("%d",&you->age);
}
int main(void) {
	person me,you;
	askabout(&me);
	printf("私の名前=%s\n私の年齢=%d\n",me.name,me.age);
	you=me;
	printf("あなたの名前=%s\nあなたの年齢=%d\n",you.name,you.age);
	return 0;
}

実行結果

名前を入力してください。
satou
年齢は?
23
私の名前=satou
私の年齢=23
あなたの名前=satou
あなたの年齢=23

meもyouも同じ構造体person型なので、そのまま代入できます。構造体同士で代入すると、メンバがすべて代入されます。C言語では、配列同士を代入することはたとえ型、要素数が同じでも不可能で数が、配列を構造体のメンバにすれば、構造体を代入することでメンバの配列も代入されます。

構造体の配列

構造体を配列にして利用することも可能です。構造体の配列の定義は普通の変数の配列と同じように

構造体型名 配列名[要素数];

とします。配列の要素のメンバにアクセスするには、

配列名[添え字].メンバ

とします。

まとめ

構造体変数は、普通の変数と同様に関数の引数として使える。

構造体の配列やポインタも同様に使用できる。

コメント

タイトルとURLをコピーしました