除了使用pragma pack aur位字段外,我们如何才能避免C中的结构填充? 还有其他方法吗?
答案 0 :(得分:6)
在结构的开头包装最大的物品。最后包装较小的物品。
struct optimal_packing
{
double d;
int i[4];
short j[3];
char s[24];
};
为了更准确一些,最需要最严格的对齐要求的项目(通常是指针和double
或者long double
),以及那些不那么严格的项目最后的对齐要求(short
和char
)。如果组件的总长度加起来(例如35个字节),您仍然可以最终得到尾部填充,但其中一种类型需要8字节对齐;有5个字节的填充。
答案 1 :(得分:2)
避免结构填充的唯一完全可移植且可靠的方法是不使用struct
中的真实成员。使用单个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];
};
便携式getter和setter看起来像这样:
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));
}
如果您知道您的体系结构接受了未对齐的访问权限,那么您可以通过使用强制转换来定义setter:
#define paddingless_get_d(o) (*(double *) ((o)->data + OFFSET_D))
#define paddingless_set_d(o, val) (*(double *) ((o)->data + OFFSET_D) = (val))
这是不可移植的,但可能比替代方案更快。它适用于 vax x86 ...
答案 2 :(得分:0)
有些事情需要考虑。首先,编译器添加填充是有原因的:它试图为其指定的平台制作最佳的工作机器代码。所以通常填充是一件好事。除了你想要定义数据通信协议,硬件寄存器映射和类似的情况,其中字节数必须简单地匹配某个规范。
实际上没有标准的方法可以避免填充。如果结构大小与其所有单个成员的总和不匹配,则可以实现的最佳结果是编译时断言给出错误。当断言失败时,您可以将编译器设置更改为阻止填充。优选:
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