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

C++ 如何获取结构或类的尾部填充的大小?,c++,c,struct,C++,C,Struct,sizeof可用于获取结构或类的大小offsetof可用于获取结构或类中字段的字节偏移量 类似地,是否有方法获取结构或类的尾部填充的大小?我正在寻找一种不依赖于结构布局的方法,例如,要求最后一个字段具有特定名称 在后台,我正在向磁盘写入一个结构,但我不想写入尾部填充,因此我需要写入的字节数是sizeof减去尾部填充的大小 澄清:不写入尾随填充的动机是为了保存输出字节。我不想保存内部填充,因为我不确定不对齐访问对性能的影响,我希望编写的代码能够低维护,这样在结构定义更改时就不需要更改。pragma

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

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

在后台,我正在向磁盘写入一个结构,但我不想写入尾部填充,因此我需要写入的字节数是sizeof减去尾部填充的大小


澄清:不写入尾随填充的动机是为了保存输出字节。我不想保存内部填充,因为我不确定不对齐访问对性能的影响,我希望编写的代码能够低维护,这样在结构定义更改时就不需要更改。

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
$
$cat struct macro.c&&echo
#包括
使用名称空间std;
#定义汇编_STRUCT3(Sname,at,a,bt,b,ct,c)结构Sname{at a;bt b;ct c;}\
int Sname###u trailingbytes(){return sizeof(struct Sname)-offsetof(Sname,c)-sizeof(ct);}
汇编结构3(S,int,i1,int,i2,char,c)
int main()
{
printf(“%d\n”,S_trailingbytes());
}
$g++-Wall-o struct macro.c&&./struct macro
3.
$

我想知道是否可以用一个C++中的可变模板类来做一些有意思的事情。但是我不太明白如何在没有宏的情况下定义类/结构和提供偏移量函数/常量——这会破坏目的。

编译器在结构中填充字段的方式在标准中没有严格定义,因此它是一种自由选择和依赖于实现的方式。 如果必须交换数据聚合,唯一的解决方案是避免任何填充
这通常使用
#pragma包(1)
完成。该杂注指示编译器将所有字段打包在一个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)
另一种方法是通过一些非标准的
#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;
}

在我看来,它使这样的代码更容易阅读。

填充可以在结构中的任何位置,除了最开始的地方。虽然
#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;
}

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

sizeof(struct)-offsetof last member-sizeof last member填充不必在后面<代码>结构x{char a;long-long b;}中间可能有7个字节填充。可能没有重复的方法,而不必引用最后一个成员的名字。不要写原始内存(比如用<代码> fDead(东西,Sigeof(1),文件)<代码> >,除非你确切地知道你在做什么。改为编写正确的序列化代码。“相当常见”选项不涉及未定义的行为,因为
sizeof
不计算其操作数。在这两种情况下,您都省略了尾部的
,因此使用这些宏的代码将无法编译。如果有人担心结构中的填充,很可能他们正在做一些系统间不可移植的事情。@Lundin然后你将100行包装在一个函数中,并调用它,就像你在回答中所做的那样。@Peter我不是在评论取消引用,而是在评论
->
。这似乎不那么明确<代码>->用于指向对象的指针,空指针常量不是一个。我相信这就是原因,例如GCC使用内置定义了
offset
,但我不确定。我数了数括号,找不到我漏掉的那一个,你能给我指出来吗?@a3f我也找不到。可能是因为他不知道一元表达式的大小是有效的C。对于复合文字等应该可以很好地工作。括号看起来像是我的错误。是的,我知道
sizeof
操作数上不需要大括号。我对不涉及未定义行为的“通用方法”的评论是站得住脚的
->
是一个解引用运算符,在
sizeof
中,一元表达式可以应用于NULL,因为表达式没有计算。我说的是尾随填充,即紧跟在最后一个字段后面的部分。另外,我正在寻找一种简单、低维护、独立于结构定义的方法。我还想避免使用
#pragma pack
,因为我不确定是什么