構造体
構造体変数とメンバ
ここでは、ポインタと同じくらい重要な要素である構造体をやりましょう
構造体 (structure) は、データの集合体を一つの変数で共有するような形になります
配列は、一つの変数名に複数のデータを代入できましたが、それは全て同じ型である必要がありました
構造体は複数の型の関連する変数を「集合」として扱えます
通常は、一つの構造体が保有するデータは論理的な関連性のあるものです
もちろん強制はされませんが、設計論として関係のないデータを
ひとつの構造体としてまとめるのは、プログラムとして欠陥と考えられます
例えば、四角形の座標を表す情報は4つの数値から成り立つと考えられます
4つの数値は、四角形の左上と右下の座標などを格納します
4つの数値は物理的な関連はありませんが、論理的には「4つで1つ」です
そこで、これらを構造体として一括して考えるのです
四角形の情報は、左上の座標 left と top 変数と右下の座標 right と bottomo からなります
これらは構造体として関連しあう変数であり、構造体のメンバと呼びます
構造体を利用するには、最初の構造体を実体化させる必要があります
構造体変数のことを、オブジェクトやインスタンスと呼ぶこともしばしばあります
構造体の宣言は struct キーワードを用い、次のような書式を持ちます
struct タグ名 {
型 メンバ1;
型 メンバ2;
型 メンバn;
.
.
.
}構造体変数リスト;
タグ名、構造体変数名は変数名同様に任意です。構造体変数リストの最後にセミコロンを忘れないでください
タグ名は後から構造体変数を新しく追加するときに必要です
構造体変数リストでの宣言のみであれば、省略しても問題はありません
では、さっそく構造体でキャラクターの名前と年齢を管理するプログラムを考えましょう
名前は文字型のポインタ、年齢は整数型がよさそうです
構造体変数名には、制御対象(キャラクター)となる変数(オブジェクト)名を指定します
struct {
char *name;
int age;
}naru , sinobu;
これで、構造体に naru と sinobu という変数が宣言されました
naruとsinobuは、構造体変数の「実体」として存在します
これらが宣言された時点で、*name や age といったメンバが使用できるようになります
逆にいえばメンバは構造体変数がなければ使えないのです
メンバは全て、構造体変数に対して定義されているためです
構造体変数が生成されると、各構造体変数にメンバが割り当てられます
それらにアクセスするには、目的の構造体変数名とメンバ名を指定します
構造体変数名.メンバ名
構造体変数名とメンバ名にはドット演算子を用います
これで宣言した構造体変数のメンバに値を代入したり参照したりすることができます
構造体の概要、アクセス方法の詳細は次のプログラムを参考にしてください。タグ名は省略しています
#include <stdio.h>
struct {
char *name;
int age;
}naru , sinobu;
int main() {
naru.name = "成瀬川なる";
naru.age = 17;
sinobu.name = "前原しのぶ";
sinobu.age = 13;
printf("名前\t\t年齢\n");
printf("%s\t%d\n" , naru.name , naru.age);
printf("%s\t%d\n" , sinobu.name , sinobu.age);
return 0;
}
たとえば、naru.name では、naru構造体変数のnameメンバにアクセスしています
メンバは構造体ごとにまったく別のメモリ領域が割り当てられることを理解してください
naru.name と sinobu.name はまったく別のメモリアドレスを指します
結果は次のようになりました
名前 年齢
成瀬川なる 17
前原しのぶ 13
構造体変数ごとにまったく別のメンバ変数が割り当てられているかどうかは、アドレスを出力させればわかりますね
構造体変数のメンバのアドレスを得るには次のように記述します
&構造体変数名.メンバ名
構造体の各メンバのアドレスの取得は、メンバ名の前ではなく構造体変数名の前に&演算子を用います
こうすれば、メンバ変数にscanf()関数を使って値を入力させることもできますね
#include <stdio.h>
struct {
char *name;
int age;
}naru , sinobu;
int main() {
printf("naru.name = %x\nnaru.age = %x\n" , &naru.name , &naru.age);
printf("sinobu.name = %x\nsinobu.age = %x\n" , &sinobu.name , &sinobu.age);
return 0;
}
それぞれまったく別のアドレスが返ってきます
構造体変数 naru と sinobu は、同じ構造体であるというだけで、メンバの実体は異なります
構造体
|
---|
構造体変数 | メンバ変数
|
---|
naru | char *name
|
int age
|
sinobu | char *name
|
int age
|
構造体変数の宣言は、構造体変数リストで一括して行っていましたが
あとで関数内で宣言する必要が出てくるかもしれません
そのような場合はタグ名をつけることで、後から構造体変数を生成することができましたね
struct タグ名 追加構造体変数名;
先ほどのプログラムに、さらにmain()関数内で motoko を追加してみましょう
#include <stdio.h>
struct LOVE_HINA {
char *name;
int age;
}naru , sinobu;
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");
printf("%s\t%d\n" , naru.name , naru.age);
printf("%s\t%d\n" , sinobu.name , sinobu.age);
printf("%s\t%d\n" , motoko.name , motoko.age);
return 0;
}
C言語ではstructキーワードを明示的に使わなければ、構造体変数を宣言できません
C++では、型が定義されていれば省略できるのでC++ユーザーには注意が必要です
いじょうが基本的な構造体の使い方です
次の章では、より細かい構造体の仕様を調べてみましょう
struct [tag] { member-list } [declarators];
構造体を定義します
tag - 構造体のタグ名を指定します(省略可)
member-list - メンバを定義します
declarators - 構造体変数を宣言します(省略可)
[struct] tag declarators;
構造体変数を宣言します
structキーワードはC++の場合のみ、省略できます
C言語の場合、省略するとエラーになるので注意が必要です
teg - 構造体のタグ名を指定します
declarators - 構造体変数を宣言します