如何获取结构或类的尾随填充的大小

How to get the size the trailing padding of a struct or class?

本文关键字:填充 何获取 获取 结构      更新时间:2023-10-16

sizeof可用于获取结构或类的大小。 offsetof可用于获取结构或类中字段的字节偏移量。

同样,有没有办法获取结构或类的尾随填充的大小?我正在寻找一种不依赖于结构布局的方法,例如要求最后一个字段具有特定名称。

对于背景,我正在写出一个结构到磁盘,但我不想写出尾随填充,所以我需要写入的字节数是大小减去尾随填充的大小。

澄清:不写入尾随填充的动机是保存输出字节。我不是在尝试保存内部填充,因为我不确定不对齐访问的性能影响,并且我希望编写代码维护成本低,以便在结构定义更改时不需要更改。

编译器在结构中填充字段的方式在标准中没有严格定义,因此它是一种自由选择和实现依赖。如果必须交换数据聚合,唯一的解决方案是避免任何填充
这通常使用 #pragma pack(1) 完成。此杂注指示编译器将所有字段打包在 1 字节边界上。 它会减慢某些处理器的访问速度,但会使结构紧凑且在任何系统上定义良好,当然,没有任何填充。

编译

指示包或等效项是执行此操作的规范方法。除此之外,我只能想到一个宏,如果成员数量是固定的或最大数量很少,比如

$ cat struct-macro.c && echo
#include<stdio.h>
using namespace std;
#define ASSEMBLE_STRUCT3(Sname, at, a, bt, b, ct, c) struct Sname {at a; bt b; ct c; }; 
       int Sname##_trailingbytes() { return sizeof(struct Sname) - offsetof(Sname, c) - sizeof(ct); }
ASSEMBLE_STRUCT3(S, int, i1, int, i2, char, c)
int main()
{
        printf("%dn", S_trailingbytes());
}
$ g++ -Wall -o struct-macro struct-macro.c && ./struct-macro
3
$

我想知道是否可以用 in C++ 的可变参数模板类做一些花哨的事情。但是我不太明白如何定义类/结构以及在没有宏的情况下再次提供偏移函数/常量 - 这将破坏目的。

填充可以在结构内的任何位置,除了在最开始的地方。没有禁用填充的标准方法,尽管某些#pragma pack是常见的非标准扩展。

如果您想要一个可靠的可移植解决方案,您实际上应该做的是为结构编写序列化/反序列化例程。

像这样:

typedef
{
  int x;
  int y;
  ...
} mytype_t;
void mytype_serialize (uint8_t* restrict dest, const mytype_t* restrict src)
{
  memcpy(dest, &src->x, sizeof(src->x)); dest += sizeof(src->x);
  memcpy(dest, &src->y, sizeof(src->y)); dest += sizeof(src->y);
  ...
}

反之亦然。

请注意,填充是有原因的。如果摆脱它,则会牺牲执行速度以增加内存大小。

编辑

奇怪的

方法,只需跳过尾随填充:

size_t mytype_serialize (uint8_t* restrict dest, const mytype_t* restrict src)
{
  size_t size = offsetof(my_type_t, y); // assuming y is last object
  memcpy(dest, src, size); 
  memcpy(dest+size, &src->y, sizeof(src->y));
  size += sizeof(src->y);
  return size;
}

您需要知道大小并对其执行有意义的操作,因为否则当您需要读回存储的数据时,您将无法知道存储数据的大小。

这是一种

可能性:

#define BYTES_AFTER(st, last) (sizeof (st) - offsetof(st, last) - sizeof ((st*)0)->last)

正如这个(C99)方法一样:

#define BYTES_AFTER(st, last) (sizeof (st) - offsetof(st, last) - sizeof (st){0}.last)

另一种方法是通过一些非标准#pragma或类似方式声明您的结构packed。这也将照顾中间的填充。

不过,这两个都不漂亮。由于不同的对齐要求,不同系统之间的共享可能不起作用。使用非标准扩展是非标准的。

只需自己进行序列化即可。也许是这样的:

unsigned char buf[64];
mempcpy(mempcpy(mempcpy(buf,
   &st.member_1, sizeof st.member_1),
   &st.member_2, sizeof st.member_2),
   &st.member_3, sizeof st.member_3);

mempcpy是一个 GNU 扩展,如果它不可用,只需自己定义它:

static inline void * mempcpy (void *dest, const void *src, size_t len) {
   return (char*)memcpy(dest, src, len) + len;
}

IMO,它使这样的代码更容易阅读。