9-3 <発展> 構造体の応用

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

構造体のネスト

構造体の中に構造体を定義して、入れ子にすることができます。

#include <stdio.h>
#include <string.h>
typedef struct{
	char name[20];
	int age;
}person;
typedef struct{
	person mother,father;
	person children[10];
	int childNum;
}family;
void askabout(person *you,const char* pos){
	person one;
	printf("%sの名前を入力してください。\n",pos);
	scanf("%s",one.name);
	puts("年齢は?");
	scanf("%d",&one.age);
	*you=one;
}
int main(void) {
	family f;
	int i;
	puts("子供は何人ですか?0から9までの数字で入力してください");
	scanf("%d",&f.childNum);
	askabout(&f.mother,"母");
	askabout(&f.father,"父");
	for (i=0;i<f.childNum;i++){
		askabout(&f.children[i],"子供");
	}
	printf("母の名前=%s\n,年齢=%d\n",f.mother.name,f.mother.age);
	printf("父の名前=%s\n,年齢=%d\n",f.father.name,f.father.age);
	for (i=0;i<f.childNum;i++){
		printf("%d番目の子供の名前=%s,年齢=%d\n",i+1,f.children[i].name,f.children[i].age);
	}
	return 0;
}

実行結果

子供は何人ですか?0から9までの数字で入力してください
2
母の名前を入力してください。
関口紗弥
年齢は?
33
父の名前を入力してください。
関口栄次郎
年齢は?
35
子供の名前を入力してください。
関口薫
年齢は?
12
子供の名前を入力してください。
関口廉人
年齢は?
9

母の名前=関口紗弥,年齢=33
父の名前=関口栄次郎,年齢=35
1番目の子供の名前=関口薫,年齢=12
2番目の子供の名前=関口廉人,年齢=9

family構造体のメンバにperson構造体があります。メンバにアクセスするにはf.mother.nameのようにメンバアクセス演算子を2回用います。

構造体のメンバに構造体を使用するときは、メンバの構造体を前もって定義しなければなりません。今回の場合は、familyのメンバにpersonがあるので、まずpersonを定義してからfamilyを定義しなければなりません。

typedef struct{
	person mother,father;
	person children[10];
	int childNum;
}family;
typedef struct{
	char name[20];
	int age;
}person;

このように定義するとpersonが定義されていないというエラーとなります。

自己参照構造体

メンバに自分自身と同じ型へのポインタを持つ構造体を自己参照構造体といいます。自己参照構造体はデータをリスト形式で保管する時に使用されます。C言語の配列では、一旦定義した後、途中の様子を削除したり途中に要素を追加したりすることはできませんが自己参照構造体を使うとそれが実現できます。

#include<stdio.h>
#include<stdlib.h>
typedef struct tagintlist{
	int data;
	struct tagintlist *next;
}intlist;
intlist* addNode(int data,intlist *node){
	intlist *new=(intlist*)malloc(sizeof(intlist));
	if(new==NULL) return NULL;
	new->data=data;
	new->next=node;
	return new;
}
void printList(intlist *node){
	while(node!=NULL){
		printf("%d\n",node->data);
		node=node->next;
	}
}
int main(void){
	intlist *node=NULL;
	node=addNode(5,node);
	node=addNode(10,node);
	node=addNode(7,node);
	printList(node);
	return 0;
}

実行結果

7
10
5

上のプログラムで、構造体の定義部分を

typedef struct tagintlist{
	int data;
	intlist *next;
}intlist;

とすると、コンパイルエラーとなります。なぜなら、メンバnextを定義している時点ではintlistのtypedef宣言が終了していないからです。これを防ぐには、構造体の定義の前にtypedef宣言をします。つまり、次のようにします。

typedef struct tagintlist intlist;
typedef struct tagintlist{
	int data;
	intlist *next;
};

ビットフィールド

ビットフィールドとは構造体のメンバに対しビット単位で制御するものです。ビットフィールドはメモリを節約するための技術で処理系によって挙動が異なる場合があります。現在のPCのメモリ量が多く、不足することはほぼないので使用する場面はほぼありません。次のように定義します。

型名 変数名: ビット単位のサイズ;

変数のうち、指定したサイズしか使われなくなります。例えば、サイズを4とすると下位4ビットしか使えず、5ビット目以降は無視されます。

#include<stdio.h>
#include<stdlib.h>
typedef struct{
	unsigned bit0: 2;
	unsigned bit1: 2;
}bitField;
int main(void){
	bitField bits;
	bits.bit0=3;
	bits.bit1=5;
	printf("bit0=%d\nbit1=%d\n",bits.bit0,bits.bit1);
	return 0;
}

実行結果

bit0=3
bit1=1

このサンプルコードではビットサイズを2としています。2の2乗は4なので4より大きい数字はオーバーフローします。5を代入する場合5を4で割った余り1が代入されます。

コメント