避免C中的结构填充

nx7onnlm  于 2023-08-03  发布在  其他
关注(0)|答案(3)|浏览(136)

除了使用杂注包或位字段外,我们如何避免C中的结构填充?还有其他方法吗?

7eumitmz

7eumitmz1#

把最大的物品放在结构的开始。最后把小的东西打包。

struct optimal_packing
{
     double d;
     int    i[4];
     short  j[3];
     char   s[24];
};

字符串
更准确地说,具有最严格的对齐要求的项需要最早出现(往往是指针和doublelong double),而那些具有较不严格的对齐要求的项则在最后出现(shortchar)。如果组件的总长度加起来达到35个字节,但其中一个类型需要8字节对齐,则仍然可以使用尾部填充;会有5个字节的填充。

b0zn9rqh

b0zn9rqh2#

避免结构填充的唯一完全可移植且可靠的方法是根本不在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上工作...

zzwlnbp8

zzwlnbp83#

有些事情需要考虑。首先,编译器添加填充是有原因的:它试图为其指定的平台制作最佳的工作机器代码。所以,正常情况下,填充是一件好事。除了您想要定义数据通信协议、硬件寄存器Map和类似的情况之外,在这些情况下,字节数必须与特定规范相匹配。
实际上没有标准的方法来避免填充。最好的方法是在编译时Assert,如果结构的大小与其所有成员的大小之和不匹配,则给出一个错误。当Assert失败时,您可以将编译器设置更改为块填充。优选地:
第一个月
如果static_assert在你的C编译器中不可用(你需要一个兼容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

字符串

相关问题