提问人:haojie zhou 提问时间:9/2/2023 最后编辑:Adrian Molehaojie zhou 更新时间:9/2/2023 访问量:95
为什么结构体 b 和结构体 d 的大小不同?
Why are the sizes of struct b and struct d different?
问:
#include <iostream>
using namespace std;
struct A{
virtual void f(){};
int a;
char ch;
};
struct B : public A{
char d;
};
struct C{
double dd;
int a;
char ch;
};
struct D : public C{
char d;
};
int main(){
B b;
D d;
cout<<"b="<<sizeof(b)<<endl;
cout<<"d="<<sizeof(d)<<endl;
}
为什么 b 和 d 的结果不同?实际结果如下:
b=16
d=24
我认为每个成员相对于结构变量地址的偏移量正好是该成员数据类型大小的倍数,最终大小是成员中最大数据类型大小的倍数。在存在继承的情况下,基类成员始终出现在派生类成员之前。此外,即使使用字节对齐方式,派生类成员也不会占用为基类保留的填充字节。换言之,一旦确定了基类的大小,这些字节就归基类独占所有,不能由派生类的成员使用。那么为什么 sizeof(b) 是 16 而不是 24?
答:
你说的关于继承基类的结构的大小可能是正确的1,尽管我不确定你所做的每个断言对于“非 POD”基类都是正确的,比如(因为成员函数)。但是,您尚未执行(并且应该执行)的是检查两个(不同)基类的大小。A
我可以在重现您的大小值的平台上编译您给定的代码(即 MSVC,面向 32 位 Windows);在这种情况下,我添加了几行来显示基类的大小:
// Earlier code as yours ...
int main() {
B b;
D d;
cout << "b=" << sizeof(b) << endl;
cout << "d=" << sizeof(d) << endl;
cout << "A=" << sizeof(A) << endl;
cout << "C=" << sizeof(C) << endl;
return 0;
}
输出:
b=16
d=24
A=12
C=16
在这里,我们看到了问题所在!有三个字段:指向类“vtable”的指针(这里显然是 32 位)、一个(也是 4 个字节)和一个(最后一个字段填充为 4 个字节);3 x 4 字节 = 12 字节;为派生类的 (padded) 字段添加 4 个字节,您就有 16 个字节。struct A
int
char
char
但是,尽管也有三个字段,但第一个字段(可能符合 IEEE 标准)的大小为 8 个字节,这会将该结构的大小增加到 16 个字节,并将从中派生到的 24 个字节。struct B
double
当我运行为 64 位系统构建的相同代码时,我得到的 和 对的大小相同,因为指针的大小与 .b
d
A
C
double
编辑:上面的解释适用于我使用的特定编译器/平台,可能是对您观察的解释。然而,正如评论中指出的(这里和问题),GNU g++ 编译器——当面向 64 位 Windows 时——为两个基类提供了相同的大小(正如预期的那样),但仍然为派生类提供了不同的大小。[在编译器资源管理器上查看]
这里的问题显然是不同的,正如评论和其中链接的各种其他 Stack Overflow 帖子中提到的,可能是因为 g++ 已经认识到它不是“POD 类型”,因此,在派生类中使用了其“尾部填充”中的空格。struct A
1 据我所知,C++ 标准并不禁止在派生类中重用尾部填充空间,即使对于“POD”类型也是如此。但是,编译器可能会避免在 POD 上这样做(就像链接示例中的 g++ 一样),因为在基类和派生类之间使用内存复制操作时,它可能会阻止优化和/或简化(此类复制操作不应用于具有虚函数的类,因为它们会涉及覆盖 vtable 条目)。
进一步的讨论在这里:C++ + gcc:尾部填充重用和POD(以及链接的帖子)。
评论
A
评论
struct B
struct D
A
B
C
D