经验

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

先看例程:
#include <iostream>
#include <vector>
#include <string.h>
#include <stdio.h>
using namespace std;

class A
{
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);
}
在VS2010中的输出结果为:
union struct的内存分配方式及其sizeof大小

分析原因:
在VS2010中,在虚继承时,派生类与基类不共享虚函数表指针,所以派生类会在基类的基础上增加一个虚函数表指针和虚基类指针。如果是多层虚继承,每一个派生类都会有自己的虚基类指针,虚基类指针是永远不会共享的。如果不是虚继承,则派生类和基类共享虚函数表指针,且派生类里不会有虚基类指针。
如果是继承多个含有虚函数的基类(非虚继承),那么基类有几个,派生类虚函数表就会有几个虚函数表指针,但是派生类本身自己不会有虚函数表指针,他跟第一个基类用一个虚函数表指针就可以,想想多继承的虚函数表结构图就理解了!!!!!
在g++中,即使虚继承,派生类也会和基类共享虚函数指针,有点怪异啊。
另外,静态成员不会占用对象内的空间。

通过在项目->属性->C/C++->命令行中添加/d1reportSingleClassLayoutE即可查看类E的内存分布情况:
1> class E size(28):
1> +---
1> 0 | {vfptr}
1> 4 | {vbptr}
1> 8 | q
1> | <alignment member> (size=1)
1> +---
1> +--- (virtual base A)
1> 12 | {vfptr}
1> 16 | a
1> | <alignment member> (size=1)
1> +---
1> +--- (virtual base A2)
1> 20 | {vfptr}
1> 24 | a2
1> | <alignment member> (size=1)
1> +---
可以看到在类E中除了两个虚继承得到的基类,还增添了一个虚函数表指针和虚基类表指针。虚函数表指针并不与基类共享。

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中,所有的共用体成员共用一个空间,并且同一时间只能储存其中一个成员变量的值。当一个共用体被声明时, 编译程序自动地产生一个变量, 其长度为联合中元类型(如数组,取其类型的数据长度)最大的变量长度的整数倍,且要大于等于其最大成员所占的存储空间。
  1. union foo  
  2. {  
  3. char s[10];  
  4. int i;  
  5. }  
在这个union中,foo的内存空间的长度为12,是int型的3倍,而并不是数组的长度10。若把int改为double,则foo的内存空间为16,是double型的两倍。
  1. union   mm{    
  2.   char   a;//元长度1        1  
  3.   int   b[5];//元长度4     20  
  4.   double   c;//元长度8     8  
  5.   int   d[3];              12  
  6.   };    
所以sizeof(mm)=8*3=24;
当在共用体中包含结构体时,如下:
  1. struct inner  
  2. {  
  3.    char      c1;  
  4.    double   d;  
  5.    char     c2;  
  6.   };  
  7.   
  8. union data4  
  9. {  
  10.      struct   inner t1;  
  11.       int           i;  
  12.       char        c;  
  13.     };  
由于data4共用体中有一个inner结构体,所以最大的基本数据类型为double,因此以8字节对齐。共用体的存储长度取决于t1,而t1长度为24,因此sizeof(uniondata4)的值为24.

当在结构体中包含共用体时,共用体在结构体里的对齐地址为共用体本身内部所对齐位数,如下:
  1. typedef union{  
  2.     long i;  
  3.     int k[5];  
  4.     char c;  
  5. }DATE;  
  6. struct data{  
  7.     int cat;  
  8.     char cc;  
  9.     DATE cow;  
  10.     char a[6];  
  11. };  
sizeof(DATE)=20, 而在结构体中中是4+1+3(补齐4对齐)+20+6+2(补齐4对齐)=36;


三、两者的区别
1. 共用体和结构体都是由多个不同的数据类型成员组成, 但在任何同一时刻, 共用体只存放了一个被选中的成员, 而结构体的所有成员都存在。
2. 对于共用体的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构体的不同成员赋值是互不影响的。


二  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所示。

union struct的内存分配方式及其sizeof大小

    考虑另外一个实例。

例二:

                                           struct X

                                           {

                                                char a;

                                                double b;

                                                int c;

                                            }S2;

    在例二中仅仅是将double型的变量和int型的变量互换了位置。测试程序不变,测试结果却截然不同,sizeof(S2)=24,不同于我们按照原则一计算出的8+8+4=20,这就引出了我们的第二原则。

   

 

    原则二:在经过第一原则分析后,检查计算出的存储单元是否为所有元素中最宽的元素的长度的整数倍,是,则结束;若不是,则补齐为它的整数倍。

    例二中,我们分析完后的存储长度为20字节,不是最宽元素长度8的整数倍,因此将它补齐到8的整数倍,也就是24。这样就没问题了。其存储示意图如图2所示。

 union struct的内存分配方式及其sizeof大小

    掌握了这两个原则,就能够分析所有数据存储对齐问题了。再来看几个例子,应用以上两个原则来判断。

例三:

                                              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所示。

 union struct的内存分配方式及其sizeof大小例四:

                                              struct X

                                              { 

                                                   double a;

                                                   char b;

                                                   int c;

                                                   char d;   

                                              }S4;

 

    仍然首先按照第一原则分析,得到的字节数为8+4+4+1=17;再按照第二原则补齐,则结构体变量S4所占存储空间为24。存储结构如图4所示:

 union struct的内存分配方式及其sizeof大小例五:

                                              struct X

                                              { 

                                                   double a;

                                                   char b;

                                                   int c;

                                                   char d;

                                                   int e; 

                                               }S5;

    同样结合原则一和原则二分析,可知在S4的基础上在结构体内部变量定义最后加入一个int型变量后,结构体所占空间并未增加,仍为24。存储结构示意图如图5所示。

union struct的内存分配方式及其sizeof大小

 

例六:

    如果将例五中加入的变量e放到第一个定义的位置,则情况就不同了。结构体所占存储空间会变为32。其存储结构示意图如图6所示。

                                              struct X

                                              { 

                                                  int e;

                                                  double a;

                                                  char b;

                                                  int c;

                                                  char d;

                                              }S6;

 union struct的内存分配方式及其sizeof大小

    补充:前面所介绍的都是元素为基本数据类型的结构体,那么含有指针、数组或是其它结构体变量或联合体变量时该如何呢?

    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。

    由于结构体所占空间与其内部元素的类型有关,而且与不同类型元素的排列有关,因此在定义结构体时,在元素类型及数量确定之后,我们还应该注意一下其内部元素的定义顺序。




例4:
  1. #include <iostream>  
  2. #pragma pack(4)//设定为4字节对齐  
  3. int main()  
  4. {  
  5.     struct Student  
  6.     {  
  7.         char a[11];  
  8.         short b;  
  9.     };  
  10.     std::cout << "struct Student \t" << "所占字节数:" << sizeof(Student) << std::endl;  
  11. }  


结果:

union struct的内存分配方式及其sizeof大小
分析:数组a按照min(sizeof(char),4),也就是1字节对其,存贮在0-10的位置。b按min(sizeof(short),4),也就是2字节对其。存储在12-13的位置。整个结构体按照min(max(sizeof(char), sizeof(short)), 4)也就是2字节对齐,14对2取整结果还是14。
数组也按照基本类型来做最后的对齐取整。(因为本质上编译器取值时还是对数组元素一个一个来处理的!!!)

三、类:
一个空类所占的字节数为1字节。
如果类中含有数据成员所占字节数规则同第二部分结构体的规则。
例1:普通类
  1. #include<iostream>  
  2. #pragma pack(4)  
  3. class Base  
  4. {  
  5.     int a;  
  6.     int b;  
  7. public:  
  8.     void CommonFunction();  
  9. };  
  10. int main()  
  11. {  
  12.     Base *base = NULL;  
  13.     std::cout << "sizeof(Base)=[" << sizeof(Base)<<"]"<< std::endl;  
  14.     return 0;  
  15. }  


结果:
union struct的内存分配方式及其sizeof大小

可见成员函数并不占用内存,只有成员变量占用内存。

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-06-04
  • 2022-01-24
猜你喜欢
  • 2022-12-23
  • 2021-07-24
  • 2022-12-23
  • 2021-10-08
  • 2021-10-01
  • 2022-12-23
  • 2021-11-27
相关资源
相似解决方案