结构体大小
首先看一下代码案例,该运行环境为Linux下的64位操作系统。代码 test.c 如下:
/******************* Author : lijd data : 2019-07-11 ********************/ #include <stdio.h> struct A{ int a; char b; short c; }T_A; struct B{ char b; int a; short c; }T_B; struct C{ char b; short c; int a; }T_C; struct D{ short d; char b; short c; int a; }T_D; struct E{ char b; struct A t_d; short c; int a; }T_E; struct F{ char b; int A_a; char A_b; short A_c; short c; int a; }T_F; int main() { printf("sizeof(int) = %d, sizeof(char) = %d, sizeof(short) = %d\n", sizeof(int), sizeof(char), sizeof(short)); printf("sizeof(T_A) = %d, sizeof(T_B) = %d, sizeof(T_C) = %d, sizeof(T_D) = %d\n", sizeof(T_A), sizeof(T_B), sizeof(T_C), sizeof(T_D)); printf("sizeof(T_E) = %d, sizeof(T_F) = %d\n", sizeof(T_E), sizeof(T_F)); return 0; }运行结果截图如下:
结论:由于编译器给结构体分配大小时,分配的大小一定是该结构体内最大系统支持类型成员(该例为int类型)的整数倍。采用字节对齐方式计算分配大小。如内嵌结构体依旧采用解析(将内置结构体解析为系统支持的类型结构)的方法去计算。
类大小
由于类的大小涉及到虚表指针的大小,虚表指针有牵扯类的继承方式,因此以下从不含继承的单类、单一继承,多重继承三个方向分析。在此之前得明白两个概念虚表指针和虚函数表:
虚表指针:指向虚函数表首地址的一个指针,存在于每个基类对象的内存中,在调用构造函数构造对象时,设置虚表指针__vfptr。
虚函数表:在编译阶段生成,编译器将类中虚函数的地址存放在虚函数表中,虚函数表存在于全局数据区.data,每个类仅有一个,供所有对象共享。不含继承的单类:
看一下代码案例,该运行环境为Linux下的64位操作系统。代码如下:
/******************* Author : lijd data : 2019-07-11 ********************/ #include<iostream> using namespace std; // 空类(实际包含构造、析构函数等) class A { }; class A_1 { int a; char b; short c; }; class A_2 { int a; char b; short c; static int d; }; class A_3 { char b; int a; short c; }; int main() { cout << "sizeof(A) : " << sizeof(A) << endl; cout << "sizeof(A_1) : " << sizeof(A_1) << endl; cout << "sizeof(A_2) : " << sizeof(A_2) << endl; cout << "sizeof(A_3) : " << sizeof(A_3) << endl; return 0; }运行结果如下:
空类即什么都没有的类,照理说大小应该是0,但是空类的大小为1,因为空类可以实例化,实例化必然在内存中占有一个位置,因此,编译器为其优化为一个字节大小。
由以上运行结果分析:不含继承的单类大小跟成员函数、静态成员变量无关,跟普通成员变量有关,关系类似于上述的结构体大小计算。
含虚函数的单一继承:
看一下代码案例,该运行环境为Linux下的64位操作系统。代码如下:
/******************* Author : lijd data : 2019-07-11 ********************/ #include<iostream> using namespace std; class B { virtual void Fun(); char a; }; class B_1: public B { int a; void Fun(); virtual void Fun1(); }; int main() { cout << "sizeof(B) : " << sizeof(B) << endl; cout << "sizeof(B_1) : " << sizeof(B_1) << endl; return 0; }运行结果如下:
由于基类B中包含一个char类型的私有变量和一个虚函数,此时B类的内存布局如下:
派生类B_1继承了基类B,它有自己的int类型的成员变量且重写了基类B中的虚函数Fun(),还定义了自己的虚函数。此时B_1类的内存布局如下:
含虚函数的多重继承:
看一下代码案例,该运行环境为Linux下的64位操作系统。代码如下:
/******************* Author : lijd data : 2019-07-11 ********************/ #include<iostream> using namespace std; class C { virtual void Fun(); virtual void Fun1(); char a; }; class D { virtual void Fun(); virtual void Fun2(); int b; }; class C_D : public C, public D { void Fun(); virtual void Fun3(); double c; }; int main() { cout << "sizeof(C) : " << sizeof(C) << endl; cout << "sizeof(D) : " << sizeof(D) << endl; cout << "sizeof(C_D) : " << sizeof(C_D) << endl; return 0; }运行结果如下:
由于基类C和基类D大小跟上面所述的含虚函数的单一继承类中的基类结构相同,因此不再赘述。派生类C_D继承了基类C和基类D,此时C_D类的内存布局如下:
C_D类自己的虚函数表指针与其声明继承顺序的第一个基类C的虚函数表指针合并,此外,若C_D类重写了基类中同名的虚函数,则在对应虚函数表的对应位置都应该予以修改。C_D中新添加的虚函数位于第一个虚函数表项最后面,C_D中新添加的成员变量位于类的最后面,按其声明顺序与内存对齐原则进行排列。
总结:
与类大小有关的因素:普通成员变量,虚函数,继承(单一继承,多重继承)
与类大小无关的因素:静态成员变量,成员函数
本文在Linux 64位系统下,分析C/C++中结构体和类的大小。结构体分配大小是最大系统支持类型成员的整数倍,采用字节对齐。类大小涉及虚表指针和继承方式,与普通成员变量、虚函数、继承有关,与静态成员变量和成员函数无关。







1374





