- 8.1 構造体
- 構造体は,複数の異なった型のデータをひとまとめにして扱いたいときに利用されます.構造体を宣言する一般形式は以下の通りです.なお,構造体の中に構造体を定義することも可能です(構造体のネスト).構造体は,C++ も中では,クラスの特別な形とみなされます.そのような意味で,構造体を使用するなら,クラス(クラスに関しては,第 10 章以降を参照してください)を使用した方が良いと思います.したがって,この節における説明も最小限にとどめたいと思います.
[記憶クラス] struct [構造体名] [{メンバー}] [変数名の並び];
- 構造体名とは,1 つの構造体に付けられた名前です.変数名の並びにあげられた各変数は,この構造体名で指定された構造を持つことになります.メンバーのリストと変数名が記述され,かつ,その構造を示す構造体名を他の場所で参照することがない場合は省略できます.
- メンバー( member )は,構造体の構造を記述する変数の並びからなります.すでに他の場所で宣言された構造体名を参照して定義する場合は省略できます.
- 変数名の並びとは,構造体名で定義された構造体型を持つ変数のリストです.配列変数を指定することも可能です.これを省略した場合は,構造が明確になるだけであり,その構造を持つ変数は定義されません.実際にこの構造を持つ変数を使用する場合は,次のように,具体的な変数を宣言する必要があります.
[記憶クラス] struct 構造体名 [変数名の並び];
- 以下,構造体の様々な定義方法の例を挙げます.例えば,次のような宣言方法があります.この構造体は,char 型の配列変数 name,sei と int 型変数 kokugo,sansu からなっています.変数 x が構造体名 student タイプの構造体であることを宣言すると共に,初期設定をしています.
struct student
{
char name[20];
char sei[3];
int kokugo;
int sansu;
} x = {"山田太郎", "男", 100, 80};
- また,構造体の宣言部分で変数名の並びを省略し,次のように書くこともできます.
struct student
{
char name[20];
char sei[3];
int kokugo;
int sansu;
};
struct student x = {"山田太郎", "男", 100, 80};
- 構造体内のメンバー値を参照・修正するには 2 つの方法があります.1 つは,「.」演算子を使用し,直接的に参照する方法です.例えば,上の構造体で name を参照するためには,
student.name
- と書きます.あと 1 つは,構造体を指すポインタ変数を定義し,そのポインタ変数に参照したい構造体のアドレスを代入した後,「->」または「.」演算子を使用して,間接的に参照する方法です.例えば,上と同じデータを参照するために,
struct student *pt;
pt = &x;
- の宣言,代入を行った後,
pt->name または (*pt).name
- と書きます.
- 次の例は,変数に配列を使用した場合で,その初期化も行っています(最初の 3 個だけが初期設定されます).
struct student
{
char name[20];
char sei[3];
int kokugo;
int sansu;
};
struct student x[50] = {
{"山田太郎", "男", 100, 80},
{"鈴木次郎", "男", 90, 100},
{"齋藤花子", "女", 90, 90}
}
- ------------------(C++)構造体宣言--------------------
- C++ においては,構造体名は,新しいデータ型の名前として,int や char と同じように使用できます.例えば,
struct data {
char *name;
int nen;
};
- のような宣言をしてあれば,
data man;
- と書くだけで,変数 man は data 型の構造体とみなされます.
- ----------------(C++)構造体宣言終わり-----------------
- (プログラム例 8.1 ) 構造体(定義と参照)
/****************************/
/* 構造体(定義と参照) */
/* coded by Y.Suganuma */
/****************************/
#include <stdio.h>
int main()
{
/*
構造体の定義と初期化
*/
struct student
{
char name[20];
char sei[3];
int kokugo;
int sansu;
};
struct student x = {"山田太郎", "男", 100, 80};
struct student y = {"山田花子", "女", 95, 90};
struct student *z;
/*
直接的参照
*/
printf("%s %s %d %d\n", x.name, x.sei, x.kokugo, x.sansu);
printf("%s %s %d %d\n", y.name, y.sei, y.kokugo, y.sansu);
/*
間接的参照
*/
z = &x;
printf("%s %s %d %d\n", z->name, z->sei, z->kokugo, z->sansu);
z = &y;
printf("%s %s %d %d\n", (*z).name, (*z).sei, (*z).kokugo, (*z).sansu);
return 0;
}
- (プログラム例 8.2 ) 構造体(ネスト,方法 1 )
- 構造体のネスト(構造体の中に構造体を定義)の例です.
/****************************/
/* 構造体(ネスト,方法1) */
/* coded by Y.Suganuma */
/****************************/
#include <stdio.h>
int main()
{
struct student
{
char name[20];
char sei[3];
struct shiken
{
int kokugo;
int sansu;
} kekka;
};
struct student x = {"山田太郎", "男", {100, 80}};
printf("名前 %s 性 %s 国語 %d 算数 %d\n",
x.name, x.sei, x.kekka.kokugo, x.kekka.sansu);
return 0;
}
- (プログラム例 8.3 ) 構造体(ネスト,方法 2 )
- プログラム例 8.2 は,次のように書くこともできます.
/****************************/
/* 構造体(ネスト,方法2) */
/* coded by Y.Suganuma */
/****************************/
#include <stdio.h>
int main()
{
struct shiken
{
int kokugo;
int sansu;
};
struct student
{
char name[20];
char sei[3];
struct shiken kekka;
};
struct student x = {"山田太郎", "男", {100, 80}};
printf("名前 %s 性 %s 国語 %d 算数 %d\n",
x.name, x.sei, x.kekka.kokugo, x.kekka.sansu);
return 0;
}
- (プログラム例 8.4 ) 構造体(関数の引数)
- 構造体も,他の変数と同様に,例えば,この例のような方法で,関数の引数として引き渡すことができます.関数 print1 へは構造体 x のコピーが渡されるため,関数 print1 の内部で構造体の値を変更しても main 関数の x には影響しません.しかし,関数 print2 では構造体 x のアドレスが渡されるため,main 関数における x と同じものを参照することになり,関数 print2 の内部で構造体の値を変更すると,main 関数における x の値も変化します.
- しかし,関数 print1 のような方法を使用すると,構造体のコピーが渡されるため,サイズが大きい構造体の場合は,実行時間的にも,メモリ的にも問題となります.また,関数 print2 のように,構造体のアドレスを渡す方法を採用すると,参照方法が面倒になります.そこで,関数 print3 のように,参照渡し(実際的には,アドレスを渡す場合と同じ)を行うことを勧めます.なお,この例においては,C++ の方法に従い,student の前の struct を省略してあります.
/****************************/
/* 構造体(関数の引数) */
/* coded by Y.Suganuma */
/****************************/
#include <stdio.h>
struct student
{
char name[20];
char sei[3];
int kokugo;
int sansu;
};
void print1(student x) // 構造体のコピーが渡される
{
x.sansu = 50;
printf(" print1 %s %s %d %d\n", x.name, x.sei, x.kokugo, x.sansu);
}
void print2(student *x) // 構造体のアドレスが渡される
{
x->sansu = 100;
printf(" print2 %s %s %d %d\n", x->name, x->sei, x->kokugo, x->sansu);
}
void print3(student &x) // 参照渡し
{
x.sansu = 70;
printf(" print3 %s %s %d %d\n", x.name, x.sei, x.kokugo, x.sansu);
}
int main()
{
student x = {"山田太郎", "男", 100, 80};
printf("main %s %s %d %d\n", x.name, x.sei, x.kokugo, x.sansu);
print1(x);
printf("main %s %s %d %d\n", x.name, x.sei, x.kokugo, x.sansu);
print2(&x);
printf("main %s %s %d %d\n", x.name, x.sei, x.kokugo, x.sansu);
print3(x);
printf("main %s %s %d %d\n", x.name, x.sei, x.kokugo, x.sansu);
}
- このプログラムを実行すると,以下に示すような出力が得られます.
main 山田太郎 男 100 80
print1 山田太郎 男 100 50
main 山田太郎 男 100 80
print2 山田太郎 男 100 100
main 山田太郎 男 100 100
print3 山田太郎 男 100 70
main 山田太郎 男 100 70
- なお,関数 print3 の内部で値を変更することを許さない場合は,
void print3(const student &x) // 参照渡し
- のような指定を行っておくべきです.
- (プログラム例 8.5 ) 構造体(リスト構造)
- この例は,図 8.1 に示すようなリスト構造を生成するプログラムです.データの追加,削除,および,出力の機能を持っています.追加されたデータは,アルファベット順に適当な位置に付け加えられます.ただし,既に存在するデータか否かのチェックは行っていません.
/****************************/
/* リスト処理 */
/* coded by Y.Suganuma */
/****************************/
#include <stdio.h>
#include <string.h>
/****************/
/* 構造体の定義 */
/****************/
struct List
{
char *st;
struct List *next;
};
/****************************************/
/* データの追加 */
/* base : 先頭の構造体へのアドレス */
/* dt : 追加する構造体へのアドレス */
/****************************************/
void add(struct List *base, struct List *dt)
{
struct List *lt1, *lt2 = base;
int k, sw = 1;
while (sw > 0) {
// 最後に追加
if (lt2->next == NULL) {
lt2->next = dt;
sw = 0;
}
// 比較し,途中に追加
else {
lt1 = lt2;
lt2 = lt2->next;
k = strcmp(dt->st, lt2->st); // 比較
if (k < 0) { // 追加
dt->next = lt2;
lt1->next = dt;
sw = 0;
}
}
}
}
/****************************************/
/* データの削除 */
/* base : 先頭の構造体へのアドレス */
/* st1 : 文字列 */
/****************************************/
void del(struct List *base, char *st1)
{
struct List *lt1, *lt2 = base;
int k, sw = 1;
while (sw > 0) {
// データが存在しない場合
if (lt2->next == NULL) {
printf(" 指定されたデータがありません!\n");
sw = 0;
}
// 比較し,削除
else {
lt1 = lt2;
lt2 = lt2->next;
k = strcmp(st1, lt2->st); // 比較
if (k == 0) { // 削除
lt1->next = lt2->next;
sw = 0;
}
}
}
}
/****************************************/
/* リストデータの出力 */
/* base : 先頭の構造体へのアドレス */
/****************************************/
void output(struct List *base)
{
struct List *nt = base->next;
while (nt != NULL) {
printf(" data = %s\n", nt->st);
nt = nt->next;
}
}
/****************/
/* main program */
/****************/
int main ()
{
int sw = 1;
char st[100];
struct List base, *lt;
base.next = NULL;
while (sw > 0) {
printf("1:追加,2:削除,3:出力,0:終了? ");
scanf("%d", &sw);
switch (sw) {
case 1: // 追加
printf(" データを入力してください ");
scanf("%s", st);
lt = new struct List;
lt->next = NULL;
lt->st = new char [strlen(st)+1];
strcpy(lt->st, st);
add(&base, lt);
break;
case 2: // 削除
printf(" データを入力してください ");
scanf("%s", st);
del(&base, st);
break;
case 3: // 出力
output(&base);
break;
default :
sw = 0;
break;
}
}
return 0;
}
- 8.2 共用体
- 共用体宣言は,同じメモリー領域を異なる型の別の変数で操作できるようにするための宣言です.その宣言方法・参照等の方法は,下に示すように,構造体とほぼ同じです.ただし,共用体の初期設定は,その最初のメンバー( member )値としてなされなければなりません.
union [共用体名] {メンバーのリスト} [変数名の並び];
- (プログラム例 8.6 ) 共用体
- 次の例は,同じ記憶域を,char,long,及び,double で共用した例です.この例からも明らかなように,ある型の値を収納してそれを別の型で参照した場合,結果は信頼できないものとなります.
/*****************************/
/* 共用体 */
/* coded by Y.Suganuma */
/*****************************/
#include <stdio.h>
int main()
{
int i1;
/*
共用体の定義(初期設定は文字)
*/
union smp
{
char ch[8];
long lg[2];
double db;
} uni = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
/* 文字として記憶 */
printf("-----char----------\n");
for (i1 = 0; i1 < 8; i1++)
printf("%c ", uni.ch[i1]);
printf("\n");
printf("%5ld %5ld\n", uni.lg[0], uni.lg[1]);
printf("%10.1f\n", uni.db);
/* long型整数として記憶 */
printf("-----long----------\n");
uni.lg[0] = 10;
uni.lg[1] = 20;
for (i1 = 0; i1 < 8; i1++)
printf("%c ", uni.ch[i1]);
printf("\n");
printf("%5ld %5ld\n", uni.lg[0], uni.lg[1]);
printf("%10.1f\n", uni.db);
/* double型として記憶 */
printf("-----double----------\n");
uni.db = 50.0;
for (i1 = 0; i1 < 8; i1++)
printf("%c ", uni.ch[i1]);
printf("\n");
printf("%5ld %5ld\n", uni.lg[0], uni.lg[1]);
printf("%10.1f\n", uni.db);
return 0;
}