C++ sizeof重读

目录

1. C++Primer中对sizeof的解释

2 如何使用sizeof呢?

2.1 sizeof (type)

2.1.1 基本数据类型的sizeof结果

2.1.2类的sizeof

2.2 sizeof  expr


首先要说明的最重要的一点就是:sizeof 是一个operator,不是函数。

1. C++Primer中对sizeof的解释

在C++Primer 第五版中有如下说明(之前囫囵吞枣,现在细细看来,里面讲了很多的细节)

细细看来有这么几点:

  1. 这一个Operator(来要看到后面一个带括号的格式,就认为他一个函数,千万不要有这个误解),

  2. 他的返回值是an expression or a type name的大小,以字节来计数,

  3. 再者,他是一个operator,那么就跟+ - * / 一样有结合规律,这里有说明,他是右结合, 从右向左。

  4. 他的返回值是一个常量表达式,而且类型是size_t。(那是不是就可以用在定义数组上面了)。
  5. it does not evaluate it's operand,有点不太好翻译,就是说他不实际计算(如表达式的数值,或者生成类)只是根据类型判断大小。有点难理解,后面看这例子。

2 如何使用sizeof呢?

2.1 sizeof (type)

2.1.1 基本数据类型的sizeof结果

cout<<"sizeof(char):"<<sizeof (char)<<endl;
//cout<<"sizeof char:"<<sizeof char <<endl;//error: expected primary-expression before ‘char’ 
cout<<"sizeof(int):"<<sizeof (int)<<endl;
cout<<"sizeof(long):"<<sizeof (long)<<endl;
cout<<"sizeof(unsigned long):"<<sizeof (unsigned long)<<endl;
cout<<"sizeof(long long):"<<sizeof(long long)<<endl;
cout<<"sizeof(short):"<<sizeof(short)<<endl;
    
cout<<"sizeof(float):"<<sizeof(float)<<endl;
cout<<"sizeof(double):"<<sizeof(double)<<endl;

cout<<"sizeof(bool):"<<sizeof(bool)<<endl;

cout<<"sizeof(int *):"<<sizeof (int*)<<endl;

在一个64位的系统中,各个数据类型的结果输出如下:

sizeof(char):1
sizeof(int):4
sizeof(long):8
sizeof(unsigned long):8
sizeof(long long):8
sizeof(short):2
sizeof(float):4
sizeof(double):8
sizeof(bool):1
sizeof(int *):8 //因为是64bit的系统,所以指针的大小为8Bytes,如果为32bit,则大小为4Bytes

2.1.2类的sizeof

1. 空类的sizeof 

如果是一个struct或者类是什么样子的结果,如果运行下面的例子:

struct A{
    A(){cout<<"Constructor A"<<endl;}
    ~A(){cout<<"De-constructor A"<<endl;}
};

....

cout<<"sizeof(A):"<<sizeof(A)<<endl;
//cout<<"sizeof A:"<<sizeof A<<endl;//error, A是一个类,属于用户自定义类型,要用sizeof (type)

他的输出结果为:

sizeof(A):1

 需要理解的一点一个类的大小是类的成员的大小与指向虚函数表的指针决定,成员函数没有关系。但是即便是一个空类,其实例化时也是要有大小的有存储空间,所以在编译时,一个空类的大小被编译器安排了一个字节

大家知道C++中的虚函数的实现是利用一个虚函数表vptr,在一个类中会就会有一个指向虚函数表的指针(如果没有虚函数,那么就没有喽)。

那为什么说成员函数不占类的空间呢呢?因为类的成员函数在实现时是存放在全局函数或者说是该命名空间中的全局函数,当然不占类的大小。所以我们不论写多少个成员函数,都不会影响,所以上面的例子中,有构造与析构也不会影响大小。

 

2. 常用类的sizeof 

如果我稍加改动一下,加上virtual函数与成员变量,

struct B{
    B(){cout<<"Constructor B"<<endl;}
    ~B(){cout<<"De-constructor B"<<endl;}
    virtual int funciton_A() = 0;
    virtual int function_B(){ return 1;}
    int a;
};

 按照上面的分析,虚函数会被存放在一个*vptr指向的表中,表可以很大,但是类中保存的只是一个指针,所以会有8Bytes的指针大小(因为是64bit系统);还有一点的是int a,我们知道他占用的空间是4Bytes, 那么是不是他的结果是12呢,其不是,他的结果是:

sizeof(B):16

为什么是16,这与字节对齐align有关,因为这是64bit的系统,按照默认的对其方式应该是64bit,那么我们的a,占了4个字节,由于对齐的原因,编译器还是会补齐到8个字节,所以总共就是16字节了。 

如果是在32bit系统,那么这个输出就是8了,因为其字节对齐就是4字节,所以4+4=8.

其的情况也类似。可以根据情况分析。

3. 字节对齐的影响

也说你会想我就是要让他紧凑的对齐,有没有办法,当然有了,使用一个关键字#pragma pack宏,如下例子

//When using pack 4, it's 12.
#pragma pack(4)
struct C{
    C(){cout<<"Constructor C"<<endl;}
    ~C(){cout<<"De-constructor C"<<endl;}
    virtual int funciton_A() = 0;
    virtual int function_B(){ return 1;}
    int a;
};
#pragma pack() //to restore to default pack style!

这个时候他的输出就是12了,见结果:

sizeof(C):12

在使用pack的时候,需要,那么在他后面定义的都会是遵从,所以如果只想一个struct或者class,则我们要使用pack()来恢复默认设置。

 

4. 子类的sizeof

还有一个情况就是有子类的话,class的大小是多少。我们可以理解为,基类大小+子类的部分

class D{
public:
    D(){cout<<"Constructor D"<<endl;}
    ~D(){cout<<"De-constructor D"<<endl;}
    virtual int funciton_A() = 0;
    virtual int function_B(){ return 1;}
    int a;
private:
    long b;
};

class DD:public D{
public:
    virtual int function_DD()= 0;
    long var;
}


......

    cout<<"sizeof(D):"<<sizeof(D)<<endl;
    cout<<"sizeof(DD):"<<sizeof(DD)<<endl;

 在这个例子中,看基类,有virtual 函数,所以会有一个8Bytes的指针,还有一个int a占用4Bytes,再一个long,占有8Bytes,在64bit系统中,默认使用的是8bytes对齐,所以占用的大小为24Bytes。

看子类,增加了一个虚函数,还有一个var long型,因为父类已经有了vptr,所以这里增加virtual函数,也没有太大的影响,再加一个long var,也就是再加一个8bytes,所以输出的结果是32.

sizeof(D):24
sizeof(DD):32

5. 类中的static函数与变量占用空间吗?

借用上面4中的例子,我们看一下。

class D{
public:
    D(){cout<<"Constructor D"<<endl;}
    ~D(){cout<<"De-constructor D"<<endl;}
    virtual int funciton_A() = 0;
    virtual int function_B(){ return 1;}
    int a;
private:
    long b;
};

class DD:public D{
public:
    virtual int function_DD()= 0;
    long var;
    static long static_flat;
    static int function_static(){};
};

添加两个static,直接看结果。

sizeof(D):24
sizeof(DD):32

 哈,没有影响,那是因为static 变量不是成员的饭后,函数是全局的,所以也不在类的里面。当然就没有影响了

6. 如果类中有bit,位域会如何呢?

结果会按照位域对齐。如果我们加一个bit:1,那么

struct Bit{
    bool b:1;
    char ch1:4;
    char ch2:4;
    long c;
};

大家猜测一下,这个会是多少呢?

答案为:16, 为什么,long占有8个,b+ch1+ch2加起来要占用9个bit,因为是8字节对齐,所以b+ch1+ch2要占用8个,所以共占用了16个字节。

【 这里还是有很多的内容,后面再慢慢补充】

2.2 sizeof  expr

struct A{
    A(){
        cout<<"constructor"<<endl;
    }
};

.....
cout<<sizeof new A<<endl; //Here, the constructor is not called!

这个例子也顺便说明了上面的第5点, it does not evaluate it's operand,这个例子中,不会创建A对象,自然构造函数不会被调用。但是这个的输出是什么呢

sizeof new A:8

因为new A会是一个指针,在64bit系统中,指针大小为8Bytes,即使A里面有纯虚函数,也可以编译通过,因为他根本就不会实例化

我迷惑的地方就是: 什么时候要用带()的表达式,什么时候不用带(), 我现在只知道如果是type(int/char/long等类型,还有就是类的类型)就用括号,如果不用括号会报错,其他的时候就不用了。

    int *ptr; 
    int array[100];

    cout<<"sizeof(pointer):"<<sizeof(ptr)<<endl;//why? ptr在我看来是一个表达式,在Linux下OK.
    cout<<"sizeof pointer:"<<sizeof ptr<<endl;
    //cout<<"sizeof void:"<<sizeof(void)<<endl;//error
    cout<<"sizeof(int *):"<<sizeof(int *)<<endl; //int * is a type
    cout<<"sizeof(array):"<<sizeof(array)<<endl;

上面指针的写法让我感到疑惑,可否有大神帮忙解惑下!

 

还有一些C++11里面的新功能,先贴在这里留个纪念吧。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值