続・構造体
配列型構造体変数
構造体変数は、他のデータ型同様に配列にすることができます
struct タグ名 構造体変数名[配列数];
配列型の各構造体変数にアクセスするには、構造体変数に添え字を指定します
また、各構造体変数の特定のメンバにアクセスするには添え字指定の後にドット演算子とメンバ名を指定します
構造体変数名[添え字].メンバ名
扱いは基本的に通常の配列と同じですね
次のプログラムを参照して、配列型構造体変数の宣言とアクセスに注目してください
#include <stdio.h>
struct LOVE_HINA {
char *name;
int age;
};
int main() {
int ary_index;
struct LOVE_HINA structNA[3];
structNA[0].name = "成瀬川なる";
structNA[0].age = 17;
structNA[1].name = "前原しのぶ";
structNA[1].age = 13;
structNA[2].name = "青山素子";
structNA[2].age = 15;
printf("なる = 0 , しのぶ = 1 , 素子 = 2>");
scanf("%d", &ary_index);
if ((ary_index >= 0) && (ary_index <= 2)) {
printf("名前\t\t年齢\n");
printf("%s\t%d" , structNA[ary_index].name , structNA[ary_index].age);
}
else printf("エラー : その番号のデータはありません");
return 0;
}
構造体変数を配列にすることによって、簡易にユーザーにアクセスする構造体を選択させることができました
このように、大量のデータを扱うことが予想される構造体において、配列は重要な要素です
構造体変数の代入
双方が同じ型の構造体変数であれば、構造体変数を他の構造体変数に代入することができます
このとき、対象となる構造体変数には当然メンバの内容も継承されます
構造体変数1 = 構造体変数2;
こうすると、構造体変数1は構造体変数2とまったく同じ物になります
このような代入を行うことで、簡易に構造体変数のコピーを取ることができますね
#include <stdio.h>
struct copy {
char str[128];
int var1;
double var2;
}st_var1;
int main() {
struct copy st_var2;
printf("文字列の入力>");
scanf("%s" , st_var1.str);
st_var1.var1 = 100;
st_var1.var2 = 10.1435;
st_var2 = st_var1;
printf("str = %s\n" , st_var2.str);
printf("var1 = %d\n" , st_var2.var1);
printf("var2 = %g" , st_var2.var2);
return 0;
}
構造体定義時に構造体変数st_var1を宣言し、main()関数でst_var1の各メンバを初期化しています
次にmain()関数で宣言された st_var2 構造体変数に st_var2 = st_var1 でst_var1を代入します
st_var2の各メンバ変数を出力した結果、st_var1のメンバ変数の内容とまったく同じと思われる結果がえられます
こうすれば、容易に構造体変数のコピーを取れますね
構造体変数と関数
構造体変数も、ローカル変数、グローバル変数の概念は通常の変数と同じです
構造体定義時に宣言された構造体変数などはグローバル変数として、どの関数からも有効ですが
特定の関数内で宣言された構造体変数においては、他の関数では使用できません
たとえば次のプログラムは間違っています
#include <stdio.h>
struct LOVE_HINA {
char *name;
int age;
}naru , sinobu;
void func(void);
int main() {
struct LOVE_HINA motoko;
naru.name = "成瀬川なる";
naru.age = 17;
sinobu.name = "前原しのぶ";
sinobu.age = 13;
motoko.name = "青山素子";
motoko.age = 15;
func();
return 0;
}
void func() {
printf("%s",motoko.name); /*エラー : 未定義のシンボル motoko*/
}
構造体変数 sinobu はグローバル変数なので問題はありませんが
main()関数内で宣言された構造体変数 motoko は func() 関数からは見えません
しかし、単体のメンバであれば、次のように処理することも可能です
#include <stdio.h>
struct LOVE_HINA {
char *name;
int age;
}naru , sinobu;
void func(char *);
int main() {
struct LOVE_HINA motoko;
naru.name = "成瀬川なる";
naru.age = 17;
sinobu.name = "前原しのぶ";
sinobu.age = 13;
motoko.name = "青山素子";
motoko.age = 15;
func(motoko.name);
return 0;
}
void func(char *name) {
printf("%s",name);
}
これは、構造体変数のメンバの内容だけを関数に渡しています
その過程は、通常の変数どうしと何ら変わらないので疑問はありませんね
しかし、構造体変数を関数で扱う場合、これはよい対策とは言えません
構造体変数全体の制御を関数で行うには
構造体変数のメンバを関数に渡すのではなく、構造体変数そのものを関数に渡す必要があります
実は、構造体変数を代入できるように、同じ型の構造体変数であれば関数間の受け渡しも可能です
構造体変数を関数に渡すには、仮引数を同じ型にする必要があります
上のサンプルプログラムを例に考えれば、次の関数のプロトタイプは有効です
void func(struct LOVE_HINA);
これで、func()関数は LOVE_HINA 型構造体変数を受け取ることができます
こうすることによって、構造体変数の各メンバの処理を関数で一括することが可能です
#include <stdio.h>
struct LOVE_HINA {
char *name;
int age;
}naru , sinobu;
void func(struct LOVE_HINA);
int main() {
struct LOVE_HINA motoko;
naru.name = "成瀬川なる";
naru.age = 17;
sinobu.name = "前原しのぶ";
sinobu.age = 13;
motoko.name = "青山素子";
motoko.age = 15;
printf("名前\t\t年齢\n");
func(naru);
func(sinobu);
func(motoko);
return 0;
}
void func(struct LOVE_HINA hina) {
printf("%s\t%d\n",hina.name , hina.age);
}
func()関数はLOVE_HINA型構造体変数を受けとり、メンバを出力する関数です
関数に構造体変数を渡すことによって、面倒な画面出力作業を一括できましたね
これで、func()関数に構造体変数を渡せば、勝手に出力してくれます
当然、ローカル変数の壁も破ることができました
main()関数で宣言された構造体変数を、別関数で受け取って処理することができます
さらに、戻り値で構造体変数を返すこともできます
方法は、やはり同じく戻り値の型を構造体の型に合わせます
上のLOVE_HINA構造体の場合は次のようにすれば良いですね
struct LOVE_HINA func(void);
これを利用すれば、面倒な構造体変数の初期化を一括することができます
たとえば、引数でメンバに初期化する値を受け取り、後は構造体変数を返せばよいのです
構造体変数は、同じ型の構造体変数であれば代入することができましたね
#include <stdio.h>
struct LOVE_HINA {
char *name;
int age;
}naru , sinobu;
void func(struct LOVE_HINA);
struct LOVE_HINA LOVE_HINA(char * , int);
int main() {
struct LOVE_HINA motoko;
naru = LOVE_HINA("成瀬川なる" , 17);
sinobu = LOVE_HINA("前原しのぶ" , 13);
motoko = LOVE_HINA("青山素子" , 15);
printf("名前\t\t年齢\n");
func(naru);
func(sinobu);
func(motoko);
return 0;
}
void func(struct LOVE_HINA hina) {
printf("%s\t%d\n",hina.name , hina.age);
}
struct LOVE_HINA LOVE_HINA(char *name , int age) {
struct LOVE_HINA hina;
hina.name = name;
hina.age = age;
return hina;
}
このプログラムでは、構造体変数を初期化するための関数 LOVE_HINA() を作りました
これで、何個構造体変数を作ろうとも、この関数を呼び出すだけなのでかなり作業効率を上げられますね
関数の名前がタグ名と同じであることにも注目してください
関数名と構造体タグ名は衝突しません
さらに、LOVE_HINA()関数を見ると、仮引数の変数名がメンバ名と同じです
しかし、メンバは構造体変数によって呼び出されるもののためメンバと変数名の衝突もありません