为什么offsetof(成员)等于sizeof(结构体)?

5

我有一个定义如下的结构体:

struct smth
{
    char a;
    int b[];
};

当我在这个结构体上调用sizeofoffsetof

cout << sizeof(struct smth) << endl;
cout << offsetof(struct smth, b) << endl;

输出结果为:

4
4

当结构体的大小为4且char使用1个字节时,为什么int数组的偏移量是4?为什么会有某种填充?另外,为什么int数组根本不占用任何空间?


1
你是指 offsetof(struct smth, b),对吧? - Carl Norum
@CarlNorum 是的,我有。已编辑。 - bugra
2
它是C还是C++?它们是不同的。 - Yu Hao
@YuHao 我特别询问的是关于C++的问题,但我添加了C标签,以便人们也可以通过该标签找到它。就这个问题的答案而言,它们有什么不同之处? - bugra
3
@biox6 你应该只使用相关的标签来标记问题。虽然C和C++在许多领域中相似,但它们在更多方面上是不同的。例如,在这个问题中,可变长度数组只在C中有效,而不是C++。 - Yu Hao
@biox6 把它变成 b[1]; 看看有什么不同。 - user2249683
3个回答

8
当结构体的大小为4且char使用1字节时,为什么int数组的偏移量是4?这是因为C标准允许填充;编译器通常对变量进行对齐以提高性能。
此外,为什么第二个变量根本不占用任何空间(似乎是这种情况)?这是一个C99灵活数组成员——这就是它的全部意义。其想法是像这样分配结构体:
struct smth *s = malloc(sizeof *s + 10 * sizeof s->b[0]);

然后您将拥有一个结构,其操作方式就像 b 是一个10元素数组一样。


这会带来什么样的性能提升?只是好奇。 - bugra
@biox6,一些处理器的指令加载只能从字对齐地址加载字。如果您有一个不对齐地址的字,您需要分别加载每个字节并重新组合。 - Carl Norum
你的分配语句没有考虑对齐约束。通常情况下,它将无法正确分配内存。相反,你应该使用 struct smth *s = malloc(offsetof(struct smth, b[10])); 分配一个适当大小的结构体。 - IInspectable
@IInspectable,怎么了? malloc需要返回安全对齐的指针,并且结构正确布局。有什么区别吗? - Carl Norum
1
可能是 10 * sizeof s->b[0] - 6502
抱歉,我的大脑有点迟钝。我想到了完全不同的东西。考虑它作为计算大小的 替代 方式,而不是 更正 。结构体中的末尾零大小数组确保适当的对齐。 - IInspectable

3
因为成员变量b的大小为零,编译器在ab成员之间添加填充以使b位于“字”边界上。
然而,如果我记得正确,像这样在结构体中拥有一个灵活数组只在C语言中有效,而在C++中只能作为编译器扩展。

int b[]和int *b是等价的吗? - Light
2
不是这样的。int* b 会使 sizeof(smth) 变大,额外的空间将用于存储某个整数变量的地址。相反,int b[] 表示该结构应动态分配一些(在编译时未知的)整数作为单个内存块。 - mity
@mity,你如何给变量b赋值?能否给我一个赋值语句? - Light
@Light 请查看Carl Norum的答案。 - Some programmer dude
@Light:假设有struct smth *s = malloc(sizeof(*s) + 10*sizeof(*s->b));,你可以编写s->a = 'c'; s->b[0] = 0; s->b[9] = 9;等代码。 - Jonathan Leffler

2

鉴于OP提到问题是关于C++的:

struct smth
{
    char a;
    int b[];
};

在C++中,像b[]这样的数组是无效的。数组必须具有固定的大小。变长数组仅在C99中有效。
假设您的编译器支持它作为扩展,那么数组b[]的大小为零,这使得结构体仅包含一个char成员。然后,struct中填充规则起作用,将struct填充到一个字(word)的大小,即4个字节在您的机器上。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接