• sizeof操作符-结构体与类大小


    导读

      sizeof是C/C++一个难点,当在自定义类上应用sizeof操作符时,总会出现意想不到的结果,下面,我们就来探讨一下sizeof这个操作符!

    目录

    1. sizeof与strlen的区别

    2. sizeof作用于结构体

    3. 字节对齐问题

    4. sizeof作用于类

    正文

    1. sizeof与strlen的区别

      这是老生常谈的问题了,下面举一个例子大家就明白了。  

    char buf[] = "hello world!";
    cout<<sizeof(buf)<<endl;
    cout<<strlen(buf)<<endl;

      output: 13   12

      结论:sizeof只计算字符串真正的长度,不带字符串结束标记('');而strlen则计算‘’

      还有一点需要注意的是,sizeof是操作符,strlen是函数,两者之间存在着根本的区别。

    2. sizeof作用于结构体/类

      先看下面程序输出的结果:

    #include<iostream>
    #include <string.h>
    
    using namespace std;
    
    typedef struct 
    {
        int a;
    }A_t;
    
    typedef struct
    {
        char a;
    }B_t;
    
    typedef struct
    {
        int a;
        char b;
    }C_t;
    
    typedef struct
    {
        int a;
        char b;
        char c;
    }D_t;
    
    
    typedef struct 
    {
        char a;
        int b;
        char c;
    }E_t;
    
    int main()
    {
        cout<<"sizeof(A_t):"<<sizeof(A_t)<<endl;
        cout<<"sizeof(B_t):"<<sizeof(B_t)<<endl;
        cout<<"sizeof(C_t):"<<sizeof(C_t)<<endl;
        cout<<"sizeof(D_t):"<<sizeof(D_t)<<endl;
        cout<<"sizeof(E_t):"<<sizeof(E_t)<<endl;
        return 0;
    }

      output:

    $./sizeof 
    sizeof(A_t):4
    sizeof(B_t):1
    sizeof(C_t):8
    sizeof(D_t):8
    sizeof(E_t):12

      为什么输出的结果和我们的猜想是不符合的呢?到底是什么在影响结构体的大小?这就是下面我们要来分析的。

      这一切的原因可以归结为字节对齐问题!

    3. 字节对齐问题

      3. 1 对齐跟数据在内存中的位置有关。

      如果一个变量的内存地址正好位于它长度的整数倍,他就被称做自然对齐。

      比如在32位cpu下,假设一个整型变量的地址为0x00000004,那它就是自然对齐的。

      3.2 为什么需要字节对齐

      需要字节对齐的根本原因在于CPU访问数据的效率问题。

    struct A
    {
        char a1;
       char a2;
    int b; char c; };

      假设上面整型变量的地址不是自然对齐,比如为0x00000002,则CPU如果取它的值的话需要访问两次内存,第一次取从0x00000002-0x00000003的一个short,

      第二次取从0x00000004-0x00000005的一个 short然后组合得到所要的数据,如果变量在0x00000003地址上的话则要访问三次内存,第一次为char,第二次为short,第三次为 char,

      然后组合得到整型数据。而如果变量在自然对齐位置上,则只要一次就可以取出数据。一些系统对对齐要求非常严格,比如sparc系统,如果取未对齐的数据会发生错误,举个例:
      

    char ch[8];
    char *p = &ch[1];
    int i = *(int *)p;

      
      运行时会报segment error,而在x86上就不会出现错误,只是效率下降。

      3.3 正确处理字节对齐

      对于标准数据类型,它的地址只要是它的长度的整数倍就行了,而非标准数据类型按下面的原则对齐:
      数组 :按照基本数据类型对齐,第一个对齐了后面的自然也就对齐了。
      联合 :按其包含的长度最大的数据类型对齐。
      结构体: 结构体中每个数据类型都要对齐。
      比如有如下一个结构体:
      

    struct stu{
       char sex;
       int length;
       char name[10];
      };
      struct stu my_stu;

      //内存分布图 

     由于在x86下,GCC默认按4字节对齐,它会在sex后面跟name后面分别填充三个和两个字节使length和整个结构体对齐。于是我们sizeof(my_stu)会得到长度为20,而不是15.

    4. sizeof作用于类

      C++的类与C的结构体存在着不同,但是同样会涉及到字节对齐的问题,在这方面是相同的,不同之处在于C++的类可以包含成员函数(包括虚函数),数据成员(静态成员)。因此,在sizeof作用于此时也有不同之处!

    4.1 空类

      当类中什么也不定义时,使用sizeof作用于该类会不会是零呢?

    class A
    {
    };
    
    sizeof(A);

      答案是不会,结果是1。很吃惊吧,那么为什么是1呢?

      我们简单分析一下:假设结果是0,那么我们定义类A的两个对象a, b时,a 和 b 占用的内存大小也应该是0,那么用什么来标识这两个不同的对象呢?显然没有任何办法。因此,这个假设不成立。

      那么就算不是0,为什么是1而不是其他数值呢?例如4。不是要考虑字节对齐吗?这是因为编译器在类中增加了一个字节。字节对齐是在含有数据成员的情况下才发生的事情,因此,在空类情况下不用考虑!

    4.1 虚函数内存分布

      虚函数的内存分布被陈皓分析的很透彻,可以去看看他的这篇博客!C++ 虚函数表解析

      下面再来看一个例子:

    class A
    {
    public:
        A()
        {
            cout<<"A()"<<endl;
        }
    
        virtual ~A()
        {
            cout<<"~A()"<<endl;
        }
    };
    
    class C
    {
    public:
        C(){
            cout<<"C()"<<endl;
        }
    
        virtual ~C() {
            cout<<"~C()"<<endl;
        }
    
        virtual void getvalue()
        {
            cout<<"getvalue()"<<endl;
        }
    };

      那么,sizeof(A)、sizeof(C)分别是什么呢?看懂了陈皓上面那篇博客你大概就应该知道了,带有虚函数的类需要用到虚函数表,而虚函数表只需要一个指针指向就OK了!因此,两者均为4

    4.2 数据成员内存分布

      同样,陈皓两篇文章很好地分析了对象的内存布局,分别是:C++ 对象的内存布局(上)C++ 对象的内存布局(下)大家可以好好看看牛逼之人是如何分析的。

      下面仅举例子说明一下:  

    class A
    {
    private:
        static int i;
    };
    
    class B
    {
    private:
        static int i;
        static double d;
        int i1;
        double d1; 
    };
    
    class C
    {
    public:
        static void fun()
        {
        }
    };

      那么sizeof(A) 和 sizeof(B)、sizeof(C)又是什么呢?答案分别是1、12、1。为什么呢?

      sizeof(A)为什么不是4呢?数据成员的类型是int整形啊,怎么就之占一个字节呢?

      这是因为,static 数据成员与成员函数都不计算入sizeof中,也就是这两种成员都是不会使得编译器在类中增加什么的。

  • 相关阅读:
    HDU 1140 War on Weather (三维点之间距离)
    HDU 1174 爆头(三维空间点与直线关系)
    POJ 2653 Pick-up sticks(计算几何 求线段交点)
    POJ 3792 Area of Polycubes(模拟)
    HDU 2372 El Dorado(DP)
    HDU 2985 Another lottery(水题)
    Radar Installation(POJ 1328 贪心)
    The Pilots Brothers' refrigerator(POJ2965枚举)
    Flip Game(枚举)
    Counterfeit Dollar (枚举)
  • 原文地址:https://www.cnblogs.com/wiessharling/p/4134657.html
Copyright © 2020-2023  润新知