zoukankan      html  css  js  c++  java
  • [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

  • 相关阅读:
    剑指offer——最小的K个数和数组中第K大的元素
    Leetcode刷题指南链接整理
    160. Intersection of Two Linked Lists
    100. Same Tree
    92. Reverse Linked List II
    94. Binary Tree Inorder Traversal
    79. Word Search
    78,90,Subsets,46,47,Permutations,39,40 DFS 大合集
    0x16 Tire之最大的异或对
    0x16 Tire
  • 原文地址:https://www.cnblogs.com/itech/p/1396740.html
Copyright © 2011-2022 走看看