プラグマパックのビットフィールドを使用する以外に、Cでの構造体のパディングを回避するにはどうすればよいですか?他に利用できる方法はありますか?
3 に答える
構造の最初に最大のアイテムを詰めます。最後に小さなアイテムを詰めます。
struct optimal_packing
{
double d;
int i[4];
short j[3];
char s[24];
};
もう少し正確に言うと、最も早く来る必要があるのは最も厳しい位置合わせ要件を持つアイテム(ポインターdouble
やおそらくlong double
)であり、最後にそれほど厳しくない位置合わせ要件を持つアイテム(short
およびchar
)です。コンポーネントの全長が合計でたとえば35バイトになる場合でも、テールパディングが発生する可能性がありますが、タイプの1つには8バイトのアライメントが必要です。5バイトのパディングがあります。
構造体のパディングを回避する唯一の完全に移植可能で信頼性の高い方法は、実際のメンバーをまったく使用しないことstruct
です。1 つのchar
配列メンバーを使用し、その内容にアクセスするマクロを定義します。
struct paddingless {
// char *p;
// double d;
// char c;
#define OFFSET_P 0
#define OFFSET_D (OFFSET_P + sizeof(char *))
#define OFFSET_C (OFFSET_D + sizeof(double))
#define OFFSET_END (OFFSET_C + sizeof(char))
char data[OFFSET_END];
};
移植可能なゲッターとセッターは次のようになります。
inline double paddingless_get_d(const struct paddingless *o) {
double val;
memcpy(&val, o->data + OFFSET_D, sizeof(val));
return val;
}
inline void paddingless_set_d(struct paddingless *o, double val) {
memcpy(o->data + OFFSET_D, &val, sizeof(val));
}
アーキテクチャがアライメントされていないアクセスを受け入れることがわかっている場合は、キャストで定義されているセッターを回避できます。
#define paddingless_get_d(o) (*(double *) ((o)->data + OFFSET_D))
#define paddingless_set_d(o, val) (*(double *) ((o)->data + OFFSET_D) = (val))
これは移植性がありませんが、別の方法よりも高速になる可能性があります。そして、それはvax x86で動作します...
考慮すべき点がいくつかあります。まず第一に、コンパイラがパディングを追加するのには理由があります。コンパイラは、指定されたプラットフォームに最適で動作するマシン コードを作成しようとします。したがって、通常、パディングは良いことです。データ通信プロトコル、ハードウェア レジスタ マッピングなどを定義する場合を除き、バイト数が単に特定の仕様に一致する必要があります。
パディングを回避する標準的な方法は実際にはありません。達成できる最善の方法は、構造体のサイズがすべての個々のメンバーの合計と一致しない場合にエラーを発生させるコンパイル時のアサートです。アサートが失敗したら、コンパイラの設定をブロック パディングに変更します。好ましい:
static_assert(sizeof(mystruct) == (sizeof(mystruct.x) + sizeof(mystruct.y) +...));
C コンパイラで static_assert を使用できない場合 (C11 準拠のコンパイラが必要です)、"ct_assert" マクロを使用してください。このサイトで十分に見つけることができます。
ただし、これは移植可能な方法で問題を解決するものではなく、コンパイラの設定に依存します。問題を解決するための真にポータブルな唯一の方法は、次のようなものです。
void mystruct_copy_from (uint8_t* raw_data, const mystruct_t* ms)
{
memcpy(raw_data, &ms->x, sizeof(ms->x));
raw_data += sizeof(ms->x);
memcpy(raw_data, &ms->y, sizeof(ms->y));
raw_data += sizeof(ms->y);
// ... and so on
}
uint8_t protocol [EXPECTED_STRUCT_SIZE];
mystruct_copy_from (protocol, &mystruct);
send(protocol); // some data communication interface without padding