• [C++对象模型][6]sizeof与对象内存布局


    有了前面几节的铺垫,本节开始摸索C++的对象的内存布局,平台为windows32位+VS2008。

    一 内置类型的size

    内置类型,直接上代码,帮助大家加深记忆:

    Code
    void TestBasicSizeOf()
    {
        cout
    << __FUNCTION__ << endl;

        cout
    << "  sizeof(char)= " << sizeof ( char ) << endl;
        cout
    << "  sizeof(int)= " << sizeof ( int ) << endl;
        cout
    << "  sizeof(float)= " << sizeof ( float ) << endl;
        cout
    << "  sizeof(double)= " << sizeof ( double ) << endl;

        cout
    << "  sizeof('$')=" << sizeof ( '$' ) << endl;
        cout
    << "  sizeof(1)= " << sizeof ( 1 ) << endl;
        cout
    << "  sizeof(1.5f)= " << sizeof ( 1.5f ) << endl;
        cout
    << "  sizeof(1.5)= " << sizeof ( 1.5 ) << endl;

        cout
    << "  sizeof(Good!)= " << sizeof ( "Good!" ) << endl ;

       
    char  str[] = "CharArray!";
       
    int  a[10]; 
       
    double  xy[10];
        cout
    << "  char str[] = \"CharArray!\"," << " sizeof(str)= " << sizeof (str) << endl;
        cout
    << "  int a[10]," << " sizeof(a)= " << sizeof (a) << endl;
        cout
    << "  double xy[10]," << " sizeof(xy)= " <<   sizeof (xy) << endl;

        cout
    << "  sizeof(void*)= " << sizeof(void*) << endl;
    }


    运行结果如下:


    二 struct/class的大小

    在C++中我们知道struct和class的唯一区别就是默认的访问级别不同,struct默认为public,而class的默认为private。所以考虑对象的大小,我们均以struct为例。对于struct的大小对于初学者来说还确实是个难回答的问题,我们就通过下面的一个struct定义加逐步的变化来引出相关的知识。

    代码如下:

    Code
    struct st1
    {
        
    short number;
        
    float math_grade;
        
    float Chinese_grade;
        
    float sum_grade;
        
    char  level;
    }; //20

    struct st2
    {
        
    char  level;
        
    short number;
        
    float math_grade;
        
    float Chinese_grade;
        
    float sum_grade;
    };//16

    #pragma pack(1)
    struct st3
    {
        
    char  level;
        
    short number;
        
    float math_grade;
        
    float Chinese_grade;
        
    float sum_grade;
    }; //15
    #pragma pack() 

    void TestStructSizeOf()
    {
        cout 
    << __FUNCTION__ << endl;

        cout 
    << "  sizeof(st1)= " << sizeof (st1) << endl;
        cout 
    << "  offsetof(st1,number) " << offsetof(st1,number) << endl;
        cout 
    << "  offsetof(st1,math_grade) " << offsetof(st1,math_grade) << endl;
        cout 
    << "  offsetof(st1,Chinese_grade) " << offsetof(st1,Chinese_grade) << endl;
        cout 
    << "  offsetof(st1,sum_grade) " << offsetof(st1,sum_grade) << endl;
        cout 
    << "  offsetof(st1,level) " << offsetof(st1,level) << endl;

        cout 
    << "  sizeof(st2)= " << sizeof (st2) << endl;
        cout 
    << "  offsetof(st2,level) " << offsetof(st2,level) << endl;
        cout 
    << "  offsetof(st2,number) " << offsetof(st2,number) << endl;
        cout 
    << "  offsetof(st2,math_grade) " << offsetof(st2,math_grade) << endl;
        cout 
    << "  offsetof(st2,Chinese_grade) " << offsetof(st2,Chinese_grade) << endl;
        cout 
    << "  offsetof(st2,sum_grade) " << offsetof(st2,sum_grade) << endl;


        cout 
    << "  sizeof(st3)= " << sizeof (st3) << endl;
        cout 
    << "  offsetof(st3,level) " << offsetof(st3,level) << endl;
        cout 
    << "  offsetof(st3,number) " << offsetof(st3,number) << endl;
        cout 
    << "  offsetof(st3,math_grade) " << offsetof(st3,math_grade) << endl;
        cout 
    << "  offsetof(st3,Chinese_grade) " << offsetof(st3,Chinese_grade) << endl;
        cout 
    << "  offsetof(st3,sum_grade) " << offsetof(st3,sum_grade) << endl;
    }

    运行结果如下;

    基于上面的对struct的测试,我们是不是有些惊呆哦,对于C++的初学者更是情不自禁的说:“我靠!原来顺序不同所占空间都不同啊,还有那个pack是啥东东啊?”,其实这里蕴含了一个内存对齐的问题,在计算机的底层进行内存的读写的时候,如果内存对齐的话可以提高读写效率,下面是VC的默认规则:

    1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
    2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍, 如有需要编译器会在成员之间加上填充字节(internal adding);
    3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。

    当然VC提供了工程选项/Zp[1|2|4|8|16]可以修改对齐方式,当然我们也可以在代码中对部分类型实行特殊的内存对齐方式,修改方式为#pragma pack( n ),n为字节对齐
    数,其取值为1、2、4、8、16,默认是8,取消修改用#pragma pack(),如果结构体某成员的sizeof大于你设置的,则按你的设置来对齐

    三 struct的嵌套

    1)实例:

    Code
    struct A
    {
        
    int i;
        
    char c;
        
    double d;
        
    short s;
    }; 
    // 24

    struct B
    {
        
    char cc;
        A a;
        
    int ii;
    }; 
    // 40

    布局:(使用VS的未发布的编译选项/d1 reportAllClassLayout 或 /d1 reportSingleClassLayout)

    2)实例:

    Code
    #pragma pack(4)
    struct A2
    {
        
    int i;
        
    char c;
        
    double d;
        
    short s;
    }; 
    // 20
    #pragma pack()

    struct B2
    {
        
    char cc;
        A2 a;
        
    int ii;
    }; 
    // 28

    布局:(使用VS的未发布的编译选项/d1 reportAllClassLayout 或 /d1 reportSingleClassLayout)

    总结:

      由于结构体的成员可以是复合类型,比如另外一个结构体,所以在寻找最宽基本类型成员时,应当包括复合类型成员的子成员,而不是把复合成员看成是一个整体。但在确定复合类型成员的偏移位置时则是将复合类型作为整体看待。

    四 空struct/class和const,static成员

    实例:

    Code
    struct empty{}; // 1
    struct constAndStatic
    {
        
    const int i;
        
    static char c;
        
    const double d;
        
    static void TestStatic(){}
        
    void TestNoStatic(){}
    }; 
    // 16

    布局:(使用VS的未发布的编译选项/d1 reportAllClassLayout 或 /d1 reportSingleClassLayout)

    上面的实例中empty的大小为1,而constAndStatic的大小为16。

    总结:

    因为static成员和函数其实是类层次的,不在对象中分配空间,而成员函数其实是被编译为全局函数了,所以也不在对象中。

    五 本节完,下次探讨虚函数对内存布局的影响!


    作者:iTech
    微信公众号: cicdops
    出处:http://itech.cnblogs.com/
    github:https://github.com/cicdops/cicdops

  • 相关阅读:
    openSUSE 13.1 Milestone 4 发布
    Neo4j 2.0 M4 发布
    iBoxDB for .NET v1.5发布, 移动NoSQL数据库
    GNU libc (Glibc) 2.18 发布
    Android 开源项目维护者宣布退出
    Jeasyframe 开源框架 稳定版 V1.5 发布
    Spring Mobile 1.1.0.RC1 和 1.0.2 发布
    Deis logo 开源PaaS系统 Deis
    EasyCriteria 3.0 发布
    TypeScript 0.9.1 发布,新增 typeof 关键字
  • 原文地址:https://www.cnblogs.com/itech/p/1396740.html
Copyright © 2020-2023  润新知