構造体 | C言語

構造体は関連するデータを格納する変数(メンバー)を、一まとめにして管理することのできる型です。配列とは異なり、同じ型で揃える必要がない点が特徴です。

[基本形]

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>を使用することで、アライメントの値を取得したり、変更したりすることが可能です。

  • パディング(隙間)の値は不定です。構造体をそのままファイルに保存したり、通信で受け渡したりしてはいけません。パディングに残っていた思わぬ情報が漏洩する恐れがあります。
  • メンバーサイズの小さい順に宣言することで隙間が小さくなり、メモリー使用量の増大を抑えられます。

コメント

コメントする

CAPTCHA


目次