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
,因为我不确定是什么