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

时间:2016-02-19 08:02:33

标签: c++ c struct

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

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

对于后台,我写了一个结构到磁盘,但我不想写出尾随填充,所以我需要写的字节数是sizeof减去的大小尾随填充。

澄清:不写尾随填充的动机是保存输出字节。我没有尝试保存内部填充,因为我不确定非对齐访问对性能的影响,我希望编写代码是低维护的,这样它就不需要如果结构定义发生变化,则更改。

4 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:1)

pragma pack或等价物是规范的方法。除此之外,我只能想到一个宏,如果成员数量是固定的或最大数量是低的,比如

$ 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("%d\n", S_trailingbytes());
}
$ g++ -Wall -o struct-macro struct-macro.c && ./struct-macro
3
$

我想知道在C ++中使用可变参数模板类是否可以做一些奇特的事情。但是我不能完全看到如何定义类/结构,并且在没有宏的情况下提供偏移函数/常数 - 这会破坏目的。

答案 2 :(得分:1)

这是可能的:

#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)

另一种方法是通过某些非标准packed或类似方式声明结构#pragma。这也可以在中间处理填充。

这两者都不是很漂亮。不同系统之间的共享可能不起作用,因为不同的对齐要使用非标准扩展是非标准的。

自己做序列化。也许是这样的:

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,它使代码更容易阅读。

答案 3 :(得分:1)

填充可以在结构内部的任何位置,除了最开始。没有标准的方法可以禁用填充,虽然#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;
}

您需要知道大小并使用它做一些有意义的事情,因为否则当您需要读取它时,您无法知道存储数据的大小。