構造体とは

こうぞうたい

C言語で、型の違う複数の値(メンバ)をひとまとめにして1つの型として扱うしくみ。struct sensor { int id; float value; }; のように定義し、変数 s のメンバは s.id、構造体へのポインタ p からは p->id で使う。メンバは宣言した順にメモリに並ぶが、アライメントのために間に詰め物(パディング)が入ることがあり、全体の大きさはメンバの大きさの合計より大きくなることがある。

「構造体」は、7分野の11問の練習問題に出てきます(命令セット・アセンブリ, C言語の基礎, メモリ階層と性能など)。

この用語の分野: C言語の基礎

定義と使い方

struct point { int x; int y; }; と定義すると、struct point p = {1, 2}; のように変数を作り、p.x や p.y で各メンバを使えます。typedef で typedef struct { … } Point; と別名を付けると、struct を書かずに Point p; と宣言できます。構造体どうしは = で丸ごと代入でき、関数の引数や戻り値にもできますが、大きな構造体はポインタで渡すのが一般的です。

メモリでの並び

メンバは宣言した順に、番地の小さい方から置かれます。int のように4バイトの境界にそろえる必要のある型の前には詰め物(パディング)が入り、構造体全体の大きさもいちばん厳しいアライメントの倍数に切り上げられます。そのため char、int、char の順に並べた構造体は、int が4バイトの一般的な環境で12バイトになり、int を先に置くと8バイトに減ります。

組込みや通信での注意

周辺回路のレジスタの並びを構造体で表すことはよく行われますが、パディングやビットフィールドの並び順は処理系によって違ってよいので、確かめずに使うと番地がずれます。通信のデータや保存ファイルの形を構造体のメモリの中身のまま扱うと、パディングやエンディアンの違いで別の環境と互換性がなくなるため、1バイトずつ組み立てる方法がよく使われます。

構造体の練習問題に挑戦

C言語のビットフィールドを使った構造体で周辺回路のレジスタを表すとき、注意が必要な点として正しいものはどれか。

  1. Aビットフィールドを使うと、プログラムが必ず2倍速くなる
  2. Bビットフィールドのビットの並び順などはコンパイラによって違ってよいので、別のコンパイラでは意図と違うビットを操作することがある
  3. Cビットフィールドは、8ビット以上の値を表せない
答えと解説を見る

正解:B. ビットフィールドのビットの並び順などはコンパイラによって違ってよいので、別のコンパイラでは意図と違うビットを操作することがある

ビットフィールドは読みやすく書けますが、C言語の規格では、構造体の中でビットを上位から並べるか下位から並べるか、欄が境界をまたぐときの扱い、アクセスの幅などが処理系に任されています。そのため、あるコンパイラで正しく動いても、別のコンパイラやCPUでは違うビットを読み書きすることがあります。マイコンの会社が用意するヘッダは、マスクとシフトで書かれていることが多いです。

よくある質問

構造体と配列の違いは何ですか?

配列は同じ型の要素を並べたもので、要素は添字で選びます。構造体は型の違うメンバをまとめたもので、メンバは名前で選びます。構造体の配列や、配列をメンバに持つ構造体のように、組み合わせて使うこともできます。

構造体の大きさを sizeof で確かめる必要があるのはなぜですか?

メンバの間や最後に入るパディングの量は、型の大きさとアライメントの決まり(ABI)によって変わるので、メンバの大きさを足しただけでは正しい大きさになりません。malloc で構造体の領域を確保するときや、ファイルに書き込むときは、sizeof(struct 名前) で実際の大きさを求めます。

解説の中の「構造体」

「構造体」が出てくる問題(11問)

  1. 上のようにx座標だけを合計する処理を、配列の構造体(各成分を別々の配列にする形)に書き換えると速くなりやすい理由はどれか。
  2. C言語のビットフィールドを使った構造体で周辺回路のレジスタを表すとき、注意が必要な点として正しいものはどれか。
  3. RISC-Vの命令「lw t0, 8(sp)」の動作として正しいものはどれか。
  4. RISC-Vの標準の呼び出し規約で、整数の引数が10個ある関数を呼ぶとき、9個目と10個目の引数はどこで渡されるか。
  5. RISC-Vで4バイトを読むlw命令の番地が4の倍数でない(境界がそろっていない)とき、起こりうることとして正しいものはどれか。
  6. ABI(アプリケーションバイナリインターフェース)の説明として正しいものはどれか。
  7. Linuxの共有ライブラリで、libfoo.so.1の「.1」のようなsonameのバージョン番号を上げるのは、一般にどのようなときか。
  8. スレッドごとのカウンタを「long cnt[8];」のように並べた配列で偽共有が起きている。対策として最も適切なものはどれか。
  9. 粒子の位置x、y、zを持つデータをGPUで処理するとき、メモリ合体アクセスの点で有利な並べ方はどれか。
  10. char は1バイト、int は4バイトで、int を4の倍数の番地にそろえて置く一般的な環境とする。上のコードを実行したときの出力として、正しいものはどれか。
  11. ヘッダファイル sensor.h を上のように書いた。#ifndef、#define、#endif の3行の目的として、正しいものはどれか。

C言語の基礎の参考書を探す PR

Amazonの検索結果へのリンクです。Amazonのアソシエイトとして、半導体教室は適格販売により収入を得ています。

関連する用語

配列 SIMD スタック 変数 呼び出し規約 引数 ポインタ アライメントとパディング ベクトル化 ビットフィールド

用語集の一覧へ