構造体は関連するデータを格納する変数(メンバー)を、一まとめにして管理することのできる型です。配列とは異なり、同じ型で揃える必要がない点が特徴です。
[基本形]
struct 構造体名 {
メンバー1;
メンバー2;
};
[例]
struct BodyMeas {
int id; // ID
double height; // 身長[cm]
double weight; // 体重[kg]
};
しかし上記例では構造体変数の宣言時に「struct BodyMeas taro;」と毎回structを付ける必要があります。このためtypedefするのが一般的です。そして構造体のメンバーにアクセスするには「.(ドット)演算子」を用います。
typedef struct {
int id; // ID
double height; // 身長[cm]
double weight; // 体重[kg]
} BodyMeas;
void func(void) {
BodyMeas taro;
taro.id = 1;
taro.height = 123.4;
taro.weigh = 34.5;
}
構造体の初期化
構造体変数を宣言と同時に初期化するには、下記のようにします。
typedef struct {
int id; // ID
double height; // 身長[cm]
double weight; // 体重[kg]
} BodyMeas;
void func(void) {
BodyMeas taro = {1, 123.4, 34.5};
...
}
void func2(void) {
BodyMeas students[2] = {
{1, 123.4, 34.5},
{2, 112.3, 32.1}
};
...
}
構造体を関数に渡す
構造体も引数として関数に渡せます。その方法として値渡しとポインター渡しの2通りの方法がありますが、ポインター渡しが一般的です。値渡しでは、各メンバーのコピーに時間を取られるためです。ただしポインター渡しの場合には注意点があります。通常各メンバーにアクセスするにはドット演算子を使用します。しかしポインターの指す構造体のメンバーにアクセスする場合にはアロー演算子(=>)を使用します。
typedef struct {
int id; // ID
double height; // 身長[cm]
double weight; // 体重[kg]
} BodyMeas;
上記構造体をポインター渡しと値渡しを行う場合の例は次のとおりです。
[ポインター渡しの例]
double Calc_BMI(const BodyMeas *student) {
double bmi;
double m = student->height / 100.0;
bmi = student->weight / (m * m);
return bmi;
}
void func(void) {
double bmi;
BodyMeas taro = {1, 123.4, 34.5};
bmi = Calc_BMI(&taro);
}
[値渡しの例]
double Calc_BMI(BodyMeas student) {
double bmi;
double m = student.height / 100.0;
bmi = student.weight / (m * m);
return bmi;
}
void func(void) {
double bmi;
BodyMeas taro = {1, 123.4, 34.5};
bmi = Calc_BMI(taro);
}
構造体配列
構造体を配列にすることで、単調な処理を繰り返し文で処理できるようになります。例えば下記のような構成を組むことで、複数あるパラメーターの制御を一括して行えます。
[file.h]
#define PARAM_NUMBER (3)
typedef struct {
long *adr; // 変数のアドレス
long min; // 範囲 最小値
long max; // 範囲 最大値
long def; // デフォルト値
} PARAM_INFO;
// パラメーター
long g_year;
long g_month;
long g_day;
void Set_ParamDefault(void);
[file.c]
// パラメーターテーブル
const PARAM_INFO m_param_table[PARAM_NUMBER] = {
{&g_year, 2000, 2099, 2000},
{&g_month, 1, 12, 1},
{&g_day, 1, 31, 1}
};
// パラメーターにデフォルト値を設定する
void Set_ParamDefault(void) {
int i;
for (i = 0; i < PARAM_NUMBER; i++) {
*m_param_table[i].adr = m_param_table[i].def;
}
}
構造体アライメント
構造体のサイズは、そのメンバーのサイズの和に必ずしも等しいわけではありません。次のような構造体があったとします。
typedef struct {
char char1;
int int1;
int int2;
double double1;
} HOGE;
int型は4バイト、double型は8バイトの処理系であったとき、この構造体のサイズは「1+4+4+8」で17バイトになりそうですが、殆どの処理系では24バイトになります。
構造体のメンバーは、ソースコードで宣言した順にメモリー上に配置されます。このときメンバーサイズの倍数となるアドレスに配置することで、ほとんどのCPUではメモリーの処理速度が向上します。このためコンパイラーが適当な境界値調整(アライメント)を行い、適切に配置します。結果、上記の構造体は下図のように配置されることになります。このアライメントのしかたは、処理系依存であることに注意してください。 なおC11で追加された<stdalign.h>を使用することで、アライメントの値を取得したり、変更したりすることが可能です。
- パディング(隙間)の値は不定です。構造体をそのままファイルに保存したり、通信で受け渡したりしてはいけません。パディングに残っていた思わぬ情報が漏洩する恐れがあります。
- メンバーサイズの小さい順に宣言することで隙間が小さくなり、メモリー使用量の増大を抑えられます。
コメント