经验
1
int main(){
union zcl {
double i;
char k[15];
char c;
};
struct date {
int cat;//元类型4
zcl cow;//元类型8,zcl里面的i double类型;
};
struct dat
{
char z2;
double z1;
};
union zc
{
char k1[25];//元类型1
char c1;//元类型1
dat z3;//元类型8,dat里面的z1 double类型;
};
cout << sizeof(zcl) << "\t" << sizeof(date) << endl;
//答案 16 24 union没有疑问,元类型整数倍,8的整数倍,且要超过最大元素的长度,即数组15个字节,最后当然是16;
//date 为24,cat 4, zcl 16,但是struct要为union最大元类型整数倍,即8的整数倍,所以最后结果是24,注意不是union 大小16整数倍,是其最大元类型整数倍。
cout << sizeof(zc) << "\t" << sizeof(dat) << endl;
//答案32 16; struct dat为16很好理解; 另外在union zc里面,最大元类型为dat里面的double为8, 要超过k1数组,且为8的整数倍,自然是32;
system("pause");
exit(0);
}
2
{
public:
char a[3];
virtual void aa(){}
//const int Size;
};
class A2
{
public:
char a2[3];
virtual void aa2() {}
};
class B :virtual public A
{
char j[3];
public:
virtual void bb() {}
};
class C :public A
{
char k[3];
public:
virtual void cc() {}
};
class D :public virtual B
{
char i[3];
public:
virtual void cc() {}
};
class E :public virtual A, public virtual A2
{
char q[3];
public:
virtual void ee() {}
};
class F :public A, public A2
{
char q[3];
public:
virtual void ee() {}
};
//int i=1;
int main()
{
A a;
B b;
C c;
D d;
E e;
F f;
//cout<<sizeof A<<endl;
cout << sizeof(a) << endl;//8
cout << sizeof(b) << endl;//20
cout << sizeof(c) << endl;//12
cout << sizeof(d) << endl;//32
cout << sizeof(e) << endl;//28 因为不涉及多层继承,(注意区分多层继承和多继承),以下为我猜的结论啊,不一定对。因为派生类与基类不共享虚函数表指针,所以派生类会在基类的基础上增加一个虚函数表指针和虚基类指针。所以,e虚拟继承a和a2,a和a2分别有一个虚函数表指针和char[3]数组,分别是8;另外因为不是多层继承,只有一层,所以只有一个虚基类指针为4;再加上e自身的虚函数表指针和数组,加起来为8;所以综上所述,结果为(3+1+4)*2+4+(4+4)=28!!
cout << sizeof(f) << endl;//20 由于每个基类都需要一个指针来指向其虚函数表,因此d的sizeof等于d的数据成员加2*8+4=20。
system("pause");
exit(0);
}
如果是继承多个含有虚函数的基类(非虚继承),那么基类有几个,派生类虚函数表就会有几个虚函数表指针,但是派生类本身自己不会有虚函数表指针,他跟第一个基类用一个虚函数表指针就可以,想想多继承的虚函数表结构图就理解了!!!!!
3
class Base1
{
public:
void f() { cout << "Base::f" << endl; }
void g1() { cout << "Base::g1" << endl; }
void h1() { cout << "Base::h1" << endl; }
};
class Base2
{
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g2() { cout << "Base::g2" << endl; }
virtual void h2() { cout << "Base::h2" << endl; }
};
class Base3
{
public:
virtual void f3() { cout << "Base::f3" << endl; }
virtual void g3() { cout << "Base::g3" << endl; }
virtual void h3() { cout << "Base::h3" << endl; }
};
class Derived : public Base1, public Base2 ,public Base3
{
public:
virtual void f() { cout << "Derived::f" << endl; }
virtual void g() { cout << "Derived::g" << endl; }
virtual void h() { cout << "Derived::h" << endl; }
};
int main()
{
Derived d;
cout << sizeof(d) << endl;//8 因为Base1没有虚函数,所以没有虚函数表指针,所以Derived的虚函数指针就只能跟Base2共享虚函数指针,另外Base3是单独的虚函数指针,所以总共是8;想一下那个多继承的虚函数表结构图,另外注意区分多继承和多层继承!!
Base1 *b1 = &d;
Base2 *b2 = &d;
Base3 *b3 = &d;
b1->f(); //Base::f()
b2->f(); //Derive::f()
4
5
6
7
一、union共用体(联合)
- union foo
- {
- char s[10];
- int i;
- }
- union mm{
- char a;//元长度1 1
- int b[5];//元长度4 20
- double c;//元长度8 8
- int d[3]; 12
- };
- struct inner
- {
- char c1;
- double d;
- char c2;
- };
- union data4
- {
- struct inner t1;
- int i;
- char c;
- };
当在结构体中包含共用体时,共用体在结构体里的对齐地址为共用体本身内部所对齐位数,如下:
- typedef union{
- long i;
- int k[5];
- char c;
- }DATE;
- struct data{
- int cat;
- char cc;
- DATE cow;
- char a[6];
- };
二 struct
一个结构体变量定义完之后,其在内存中的存储并不等于其所包含元素的宽度之和。
例一:
#include <iostream>
using namespace std;
struct X
{
char a;
int b;
double c;
}S1;
void main()
{
cout << sizeof(S1) << endl;
cout << sizeof(S1.a) << endl;
cout << sizeof(S1.b) << endl;
cout << sizeof(S1.c) << endl;
}
比如例一中的结构体变量S1定义之后,经测试,会发现sizeof(S1)= 16,其值不等于sizeof(S1.a) = 1、sizeof(S1.b) = 4和 sizeof(S1.c) = 8三者之和,这里面就存在存储对齐问题。
原则一:结构体中元素是按照定义顺序一个一个放到内存中去的,但并不是紧密排列的。从结构体存储的首地址开始,每一个元素放置到内存中时,它都会认为内存是以它自己的大小来划分的,因此元素放置的位置一定会在自己宽度的整数倍上开始(以结构体变量首地址为0计算)。
比如此例,首先系统会将字符型变量a存入第0个字节(相对地址,指内存开辟的首地址);然后在存放整形变量b时,会以4个字节为单位进行存储,由于第一个四字节模块已有数据,因此它会存入第二个四字节模块,也就是存入到4~8字节;同理,存放双精度实型变量c时,由于其宽度为8,其存放时会以8个字节为单位存储,也就是会找到第一个空的且是8的整数倍的位置开始存储,此例中,此例中,由于头一个8字节模块已被占用,所以将c存入第二个8字节模块。整体存储示意图如图1所示。
考虑另外一个实例。
例二:
struct X
{
char a;
double b;
int c;
}S2;
在例二中仅仅是将double型的变量和int型的变量互换了位置。测试程序不变,测试结果却截然不同,sizeof(S2)=24,不同于我们按照原则一计算出的8+8+4=20,这就引出了我们的第二原则。
原则二:在经过第一原则分析后,检查计算出的存储单元是否为所有元素中最宽的元素的长度的整数倍,是,则结束;若不是,则补齐为它的整数倍。
例二中,我们分析完后的存储长度为20字节,不是最宽元素长度8的整数倍,因此将它补齐到8的整数倍,也就是24。这样就没问题了。其存储示意图如图2所示。
掌握了这两个原则,就能够分析所有数据存储对齐问题了。再来看几个例子,应用以上两个原则来判断。
例三:
struct X
{
double a;
char b;
int c;
}S3;
首先根据原则一来分析。按照定义的顺序,先存储double型的a,存储在第0~7个字节;其次是char型的b,存储在第8个字节;接下来是int型的c,顺序检查后发现前面三个四字节模块都被占用,因此存储在第4个四字节模块,也就是第12~15字节。按照第一原则分析得到16个字节,16正好是最宽元素a的宽度8的整数倍,因此结构体变量S3所占存储空间就是16个字节。存储结构如图3所示。
例四:
struct X
{
double a;
char b;
int c;
char d;
}S4;
仍然首先按照第一原则分析,得到的字节数为8+4+4+1=17;再按照第二原则补齐,则结构体变量S4所占存储空间为24。存储结构如图4所示:
例五:
struct X
{
double a;
char b;
int c;
char d;
int e;
}S5;
同样结合原则一和原则二分析,可知在S4的基础上在结构体内部变量定义最后加入一个int型变量后,结构体所占空间并未增加,仍为24。存储结构示意图如图5所示。
例六:
如果将例五中加入的变量e放到第一个定义的位置,则情况就不同了。结构体所占存储空间会变为32。其存储结构示意图如图6所示。
struct X
{
int e;
double a;
char b;
int c;
char d;
}S6;
补充:前面所介绍的都是元素为基本数据类型的结构体,那么含有指针、数组或是其它结构体变量或联合体变量时该如何呢?
1.包含指针类型的情况。只要记住指针本身所占的存储空间是4个字节就行了,而不必看它是指向什么类型的指针。
例七:
struct X struct Y struct Z
{ { {
char *a; int *b; double *c;
}; }; };
经测试,可知sizeof(X)、sizeof(Y)和sizeof(Z)的值都为4。
2.含有构造数据类型(数组、结构体和联合体)的情况。首先要明确的是计算存储空间时要把构造体看作一个整体来为其开辟存储空间;其次要明确的是在最后补齐时是按照所有元素中的基本数据类型元素的最长宽度来补齐的,也就是说虽然要把构造体看作整体,但在补齐的时候并不会按照所含结构体所占存储空间的长度来补齐的(即使它可能是最长的)。
例八:
struct X
{
char a;
int b;
double c;
};
struct Y
{
char a;
X b;
};
经测试,可知sizeof(X)为16,sizeof(Y)为24。即计算Y的存储长度时,在存放第二个元素b时的初始位置是在double型的长度8的整数倍处,而非16的整数倍处,即系统为b所分配的存储空间是第8~23个字节。
如果将Y的两个元素char型的a和X型的b调换定义顺序,则系统为b分配的存储位置是第0~15个字节,为a分配的是第16个字节,加起来一共17个字节,不是最长基本类型double所占宽度8的整数倍,因此要补齐到8的整数倍,即24。测试后可得sizeof(Y)的值为24。
由于结构体所占空间与其内部元素的类型有关,而且与不同类型元素的排列有关,因此在定义结构体时,在元素类型及数量确定之后,我们还应该注意一下其内部元素的定义顺序。
- #include <iostream>
- #pragma pack(4)//设定为4字节对齐
- int main()
- {
- struct Student
- {
- char a[11];
- short b;
- };
- std::cout << "struct Student \t" << "所占字节数:" << sizeof(Student) << std::endl;
- }
- #include<iostream>
- #pragma pack(4)
- class Base
- {
- int a;
- int b;
- public:
- void CommonFunction();
- };
- int main()
- {
- Base *base = NULL;
- std::cout << "sizeof(Base)=[" << sizeof(Base)<<"]"<< std::endl;
- return 0;
- }