構造体の宣言にpackedを指定する
| 設計内容分類 | 組み込みソフトウェアにおけるC言語記述 |
|---|
実現したい仕様・課題
構造体を通信フレームや外部保存データ、メモリマップされたデバイスのレイアウト定義として使う場合、コンパイラがアラインメント(整列)のためにパディングバイトを自動挿入し、構造体のサイズがメンバ変数の合計サイズと一致しないことがあります。パディングの入り方は処理系依存のため、通信相手や保存フォーマットとバイト位置がずれ、データ化けや互換性問題の原因になります。構造体のメモリレイアウトを意図どおりに制御したいというのが課題です。
設計のポイント
構造体の宣言時に __packed や __attribute__((__packed__)) を指定することで、パディングバイトの挿入を抑制し、構造体サイズをメンバ変数の合計サイズに一致させることができます(指定方法はコンパイラによって異なります)。たとえば char a; int b; を持つ構造体は、通常はaの後に3バイトのパディングが入り8バイトになりますが、packed指定により5バイトちょうどになります。通信フレームの構造体定義や、外部メモリに保存するデータレイアウトなど、バイト配置が仕様で決まっている用途で有効です。一方で注意点もあります。packedにするとメンバが非整列アドレスに配置されるため、CPUによってはアクセスが遅くなり、非整列アクセスを許さないCPUではフォルト(例外)の原因になり得ます。したがってpacked構造体は「外部とのレイアウト互換が必要な境界部分」に限定し、内部処理では通常の構造体へコピーして扱う構成が安全です。また、そもそもメンバをサイズの大きい型から順に並べることでパディング自体を減らす設計も有効です。sizeofで意図どおりのサイズになっているかをコード上で検証しておくと、コンパイラ変更時の差異も早期に検出できます。なお、packedはバイト順(エンディアン)までは制御しない点にも注意が必要です。

【コード例】
// 通常宣言:パディングが入りsizeofは8になることが多い
typedef struct {
char a; // +0
int b; // +4(+1〜+3はパディング)
} st_data;
// packed指定:sizeofは5(GCC系の例)
typedef struct __attribute__((__packed__)) {
char a; // +0
int b; // +1〜+4
} st_data_packed;
補足情報
設計のポイント 一覧
組み込みソフトウェアにおけるC言語記述のポイント
- リングバッファで受信データを取りこぼさない
- switch-caseによるステートマシンの実装
- 割り込みハンドラは短く書く
- constを活用する(ROM配置と書き換え防止)
- ビット演算によるレジスタ・フラグ操作
- 固定幅整数型(stdint.h)を使う
- volatile修飾子を正しく使う
- 構造体の宣言にpackedを指定する
- ソースコードのエンディアン変換
- sizeofを使用する
- 特殊文字の記述にエスケープ文字を使用する
- 変数の初期化漏れを防ぐ
- 外部バスの使用
- sprintf関数を使う
- 列挙型(enum)を使う
- 構造体を使う
- 標準関数を使う
- 条件演算子を使う
- インクリメント演算子を使う
- C言語記述の際はアプリ層とドライバ層に分ける



