sizeof
可用于获取结构或类的大小。 offsetof
可用于获取结构或类中字段的字节偏移量。
同样,有没有办法获得结构或类的尾部填充的大小?我正在寻找一种不依赖于结构布局的方法,例如:要求最后一个字段具有特定名称。
对于后台,我写了一个结构到磁盘,但我不想写出尾随填充,所以我需要写的字节数是sizeof减去的大小尾随填充。
澄清:不写尾随填充的动机是保存输出字节。我没有尝试保存内部填充,因为我不确定非对齐访问对性能的影响,我希望编写代码是低维护的,这样它就不需要如果结构定义发生变化,则更改。
答案 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;
}
您需要知道大小并使用它做一些有意义的事情,因为否则当您需要读取它时,您无法知道存储数据的大小。