sizeof用法大总结(下)

来源:互联网 发布:react服务器端渲染 seo 编辑:程序博客网 时间:2024/05/16 11:27

以下结论均是在32位系统VS2010下运行(关于类的部分是个人的初步理解,若不对,望指正!)

6、  结构体及位域

当使用sizeof计算结构体所占内存大小时,遵循字节对齐原则,而默认的字节对齐遵循以下原则:

若结构体内的最大数据类型所占的内存大小小于计算机字长时,以结构体内最长数据类型为对齐单位进行字节对齐;

若结构体内的最大数据类型所占的内存大小大于计算机字长时,以计算机字长为对齐单位进行字节对齐;

struct A{char a;char c;short b;};struct B{char a;short b;char c;};struct C{char a;int b;double c;};cout<<sizeof(A)<<endl;  // 4 (见解释1)cout<<sizeof(B)<<endl;  // 6 (见解释2)cout<<sizeof(C)<<endl;  // 16 (见解释3)

背景知识:由于结构体A和B中的最长数据类型为short型(占2个字节),小于32位计算机的字长,所以结构体A和B均以2字节为单元进行对齐(也即最后输出结果为2的倍数)。

解释1:结构体A中,变量a和c各占1个字节,刚好处于2字节单位的字节单元中(该处字节单元为2个字节),加上b占2字节,共4字节。如图1所示:

a(1)

c(1)


                 

a(1)

填充(1)

b(2)

b(2)

              图1

c(1)

填充(1)

                                                                                                                                            图2

解释2:结构体A中,变量a占1个字节,变量b占2字节,变量c占1字节。由于变量b在中间,所以需要进行内存填充,达到内存对齐的目的,变量a后填充1字节,对齐到2字节,变量b不需要填充,变量c填充1字节对齐到2字节。所以最后输出为6字节。如图2所示。

解释3:在结构体C中,存在int型和double型,大于32位计算机的字长,所以以默认的计算机字长(4字节)为对齐单位,所以一共是1(char) + 3(填充) + 4(int) + 8(double) = 16

位域:

struct D{char a : 5;char b : 2;short c : 1;};struct E{char a : 5;short c : 1;char b : 2;};struct F{char a : 5;int c : 1;char b : 2;};cout<<sizeof(D)<<endl;  // 4 (见解释4)cout<<sizeof(E)<<endl;  // 6 (见解释5)cout<<sizeof(F)<<endl;  // 12 (见解释6)

解释4:char类型的变量a占5个bit位,b占2个bit位,由于a和b都是char型,所以,一个char型即可放得下。Short型变量c占1个bit位,因为跟a和b不是同一类型,所以得从下一个字节开始存放,因此sizeof(D) = 5bit + 2bit + 9bit(填充) + 1bit + 15bit(填充) = 4字节。

解释5:参考解释1,2,4

解释6:参考解释3,4


7、  联合体

联合体所占的内存空间大小等于联合体中最大数据类型所占的内存空间大小,因为联合体中的各变量是共享内存的。

#include <iostream>using namespace std;union A{char a;short b;};void main(){cout<<sizeof(A)<<endl;  // 2}

因为联合体A中最大数据类型为short型,占2个字节的内存,所以A的内存大小为2字节。


8、  枚举类型

对于枚举类型,其中的元素本质上是int型常量,所以等价于sizeof(int)=4,为什么不是N*sizeof(int)(N为枚举个数),个人猜测是枚举类型是将所有可能的取值都罗列出来,而每次只能取一个值,所以占用的内存与sizeof(int)等价。

#include <iostream>using namespace std;enum A{one, two, three};void main(){cout<<sizeof(A)<<endl;  // 4}

 

9、  类

例1:

class A{private:int Aa;static int Ab;};cout<<sizeof(A)<<endl;  // 4

对于类A,静态变量Ab是在静态数据区分配内存的,不在栈中分配内存,因此不在sizeof运算符的计算范围内,所以输出为4

例2:

class E{public:virtual void fun(){cout<<"E::fun()"<<endl;}private:int Ea;};class F : public E{public:virtual void fun(){cout<<"F::fun()"<<endl;}private:int Fa;};cout<<sizeof(E)<<endl;  // 8cout<<sizeof(F)<<endl;  // 12

由于类E中含有虚函数,所以存在虚函数指针,占4个字节,加上变量Ea占4个字节,共8个字节。

对于类F,由于类F继承自E,所以F中的fun函数也是虚函数,故将fun函数的入口地址加入到虚表中(在子类中,子类与父类共享虚表),所以sizeof(F) = sizeof(E) + sizeof(Fa) = 12(个人理解,若不对,请指正!)

例3:

class E{public:virtual void fun(){cout<<"E::fun()"<<endl;}private:int Ea;};class F : virtual public E{public:virtual void fun(){cout<<"F::fun()"<<endl;}private:int Fa;};cout<<sizeof(E)<<endl;  // 8cout<<sizeof(F)<<endl;  // 16

例3与例2相比,例3增加了虚继承,类F中的fun是E中的重写,实现多态,因此同例2,同样将F中的fun加入到虚表中,但是新增了一个指向类E的虚指针(暂称为虚继承指针),所以sizeof(F) = sizeof(E) + sizeof(Fa) + sizeof(虚继承指针) = 16

另外一种计算方法:sizeof(F) = sizeof(Ea) + sizeof(Fa) + sizeof(虚继承指针) + sizeof(虚表指针) = 16

疑问:上面两种计算方法,到底正不正确,还望各位指出!

例4:

class E{public:virtual void fun(){cout<<"E::fun()"<<endl;}private:int Ea;};class F : virtual public E{public:virtual void fun1(){cout<<"F::fun1()"<<endl;}private:int Fa;};cout<<sizeof(E)<<endl;  // 8cout<<sizeof(F)<<endl;  // 20

例4与例3相比,例4将F中的fun函数改为了fun1函数,此时由于fun1与fun函数名称不一样,所以会在F中新建一个虚表指针,同事新增了一个指向类E的虚指针(暂称为虚继承指针),所以

计算方法1:sizeof(F) = sizeof(E) + sizeof(Fa) + sizeof(F中虚表指针) + sizeof(虚继承指针) = 20

计算方法2:sizeof(F) = sizeof(Ea) + sizeof(Fa) + sizeof(E中虚表指针) + sizeof(F中虚表指针) + sizeof(虚继承指针) = 20

 疑问:上面两种计算方法,到底正不正确,还望各位指出!

例5:

class A{private:int Aa;static int Ab;};class B : virtual public A{private:int Ba;static int Bb;};class C : virtual public A{private:int Ca;static int Cb;};class D : virtual public B, virtual public C{private:int Da;static int Db;};cout<<sizeof(A)<<endl;  // 4cout<<sizeof(B)<<endl;  // 12cout<<sizeof(C)<<endl;  // 12cout<<sizeof(D)<<endl;  // 28

对于多重虚继承,只会在派生类中保留一份基类的拷贝。

sizeof(A) = 4

sizeof(B) = sizeof(A) + sizeof(Ba) + sizeof(虚指针) =4+4+4=12

sizeof(C) = sizeof(A) + sizeof(Ca) + sizeof(虚指针) =4+4+4=12

sizeof(D) = sizeof(B) + sizeof(C) + sizeof(Da) + sizeof(虚指针) –sizeof(A)= 12+12+4+4-4=28

计算sizeof(D)的时候,减去sizeof(A)是因为多重虚继承,派生类D中,只会保留一份A,继承图形如下:

            A

          /     \

        B       C

          \      /

            D

例6:

class G{};class H : public G{};cout<<sizeof(G)<<endl;  // 1cout<<sizeof(H)<<endl;  // 1

空类所占的内存空间大小为1个字节。为了确保两个不同对象的地址不同,必须如此。

类的实例化是在内存中分配一块地址,每个实例在内存中都有独一无二的二地址。同样,空类也会实例化,所以编译器会给空类隐含的添加一个字节,这样空类实例化后就有独一无二的地址了。所以,空类的sizeof为1,而不是0。

0 0
原创粉丝点击