您的位置:首页 > 其它

sizeof计算对象所占内存的大小详解

2016-11-17 22:23 761 查看
首先声明sizeof并非为一个函数,而是C语言中的一个关键字, sizeof计算对象所占内存的大小,判断操作数的类型长度,以字节为单位;地址

的大小在32位系统占4个字节,在64位系统占8个字节,本实例都在32位机器上操作;数组名arr表示整个数组两种情况:sizeof(arr)以及 &arr。

定义一个整型数组的情况

#include <stdio.h>
int main()
{
int arr[8] = { 0 };
printf("%d ", sizeof(arr) / sizeof(arr[0]));      //8   (1)
printf("%d ", sizeof(arr));                      //32   (2)
printf("%d ", sizeof(arr+0));                   //4     (3)
printf("%d ", sizeof(*arr));                   //4      (4)
printf("%d ", sizeof(arr+1));                 //4       (5)
printf("%d ", sizeof(arr[0]));               //4        (6)
printf("%d ", sizeof(&arr));                //4         (7)
printf("%d ", sizeof(&arr+1));             //4          (8)
printf("%d ", sizeof(&arr[0]+1));         //4           (9)
printf("%d ", sizeof(*&arr));            //32           (10)
return 0;
}


(1)求数组元素的个数,数组元素总数为8个

(2)数组名arr代表整个数组;整个数组占用的内存空间大小为32

(3)此时arr+常量;数组名arr表示为数组首元素的地址,所以此时还是数组首元素,地址的大小为4(在32位机器中)

(4)此时的数组名arr为数组首元素的地址,然后再对其*,代表为首元素,首元素为整型,占4个字节

(5)第二个元素的地址,解释同(2)

(6)数组中第一个元素,第一个元素为整型,占4个字节

(7)数组的地址,地址在32位占4个字节,在64位占8个字节

(8)跳过整个数组,依然为地址.

(9)第二个元素的地址,地址所占内存大小依然为4个字节

(10)&arr表示数组地址,再对其*,表示访问整个数组,整个数组所占内存空间大小为32个字节.

2.定义一个字符型数组

#include <stdio.h>
int main()
{
char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
printf("%d ", sizeof(arr) / sizeof(arr[0]));       //8   (1)
printf("%d ", sizeof(arr));                       //8    (2)
printf("%d ", sizeof(arr+0));                    //4     (3)
printf("%d ", sizeof(*arr));                    //1      (4)
printf("%d ", sizeof(arr+1));                  //4       (5)
printf("%d ", sizeof(arr[0]));                //1        (6)
printf("%d ", sizeof(&arr));                 //4         (7)
printf("%d ", sizeof(&arr+1));              //4          (8)
printf("%d ", sizeof(&arr[0]+1));          //4           (9)
printf("%d ", sizeof(*&arr));             //8           (10)
return 0;
}


(1)求数组元素的个数

(2)数组名arr代表整个数组;整个数组占用的内存空间大小为8,一个字符占一个字节

(3)此时arr+常量;数组名arr表示为数组首元素的地址,所以此时还是数组首元素的地址,地址的大小为4.

(4)此时的数组名arr为数组首元素的地址,然后再对其*,代表为首元素,首元素为字符型,占1个字节

(5)第二个元素的地址,解释同(2)

(6)数组中第一个元素,第一个元素为字符型,占1个字节

(7)数组的地址,地址在32位系统占4个字节.

(8)跳过整个数组,依然为地址.

(9)第二个元素的地址,地址所占内存大小依然为4个字节

(10)&arr表示数组地址,再对其*,表示访问整个数组,整个数组所占内存空间大小为8个字节.

3.定义一个char *arr=”abcdefgh”.

#include <stdio.h>
int main()
{
char *arr = "abcdefgh";
printf("%d ", sizeof(arr));                //4   (1)
printf("%d ", sizeof(arr+1));             //4    (2)
printf("%d ", sizeof(*arr));             //1     (3)
printf("%d ", sizeof(arr[0]));          //1      (4)
printf("%d ", sizeof(&arr));           //4       (5)
printf("%d ", sizeof(&arr+1));        //4        (6)
printf("%d ", sizeof(&arr[0]+1));    //4         (7)
return 0;
}


(1)arr相当于一个指针变量即地址,大小为4个字节

(2)还是地址,所以占四个字节.

(3)arr为地址,对其*,则为里面的元素,元素为字符型,所以占一个字节

(4)相当于第一个元素,为字符型占一个字节.

(5)对地址再进行取地址,还是地址,所以大小为4个字节.

(6) 还是地址,所以占四个字节.

(7)数组元素的地址,地址在32位占4个字节.

4.定义一个二维数组的情况

#include <stdio.h>
int main()
{
int arr[3][4] = { 0 };
printf("%d ", sizeof(arr));               //48   (1)
printf("%d ", sizeof(arr[0][0]));        //4     (2)
printf("%d ", sizeof(arr[0]));          //16     (3)
printf("%d ", sizeof(arr[0]+1));       //4       (4)
printf("%d ", sizeof(arr+1));         //4        (5)
printf("%d ", sizeof(&arr[0]+1));    //4         (6)
printf("%d ", sizeof(*arr));        //16         (7)
printf("%d ", sizeof(arr[4]));     //16          (8)
printf("%d ", sizeof(*arr+1));    //4            (9)
return 0;
}


(1)求整个数组所占内存空间,共12个整型元素,一个整型元素占4个字节

(2)求数组的第一个元素所占内存空间大小,一个整型占4个字节

(3)相当于第一行元素所占内存空间的大小,第一行共有4个整型元素

(4)arr[0]+常量,arr[0]为数组首元素地址,所以arr[0]+1为数组第二个元素的地址

(5)单独一个arr应理解为首元素地址,在二维数组中即为第一行地址(了解二维数组存放方式),所以arr+1为第二行起始位置的地址

(6)数组第二行的地址,将arr[0]看成一个数组名,arr[1]为下一个数组名,所以(&arr[0]+1)为第二行数组元素的地址

(7)单独一个arr为数组第一行的地址,再对其*,则为数组第一行的内容,所占字节为16

(8)arr[4]相当于第四行元素所占字节,虽然没有第四行,但是sizeof求值只看类型,不看表达式是否正确

(9)第一行第二个元素为整型,占四个字节

5.类的对象的大小计算

#include <iostream>
using namespace std;
//(1)定义一个普通的类
class A
{
public:
void f1()
{
cout << "A::f1()" << endl;
}
int _a;
};

//(2)定义一个含有虚函数的类
class  B
{
public:
virtual void f1()
{
cout << "B::f1()" << endl;
}
int _b;
};

//(3)定义一个继承B类的子类,并且本身还有独立虚函数的类
class C :public B
{
public:
virtual void f1()
{
cout << "C::f1()" << endl;
}
virtual void f2()
{
cout << "C::f2()" << endl;
}
int _c;
};

class D :public B
{
public:
virtual void f1()
{
cout << "D::f1()" << endl;
}
virtual void f2()
{
cout << "D::f2()" << endl;
}
int _d;
};

//(4)定义一个菱形继承,不存在虚继承,且基类拥有没有被重写的虚函数
class E :public C,public D
{
public:
virtual void f1()
{
cout << "E::f1()" << endl;
}
virtual void f2()
{
cout << "E::f2()" << endl;
}
virtual void f3()
{
cout << "E::f3()" << endl;
}
int _e;
};
//(5)定义一个菱形继承且为虚继承,且子类存在没有被重写的虚函数
class C1 : virtual public B
{
public:
virtual void f1()
{
cout << "C1::f1()" << endl;
}
virtual void f2()
{
cout << "C1::f2()" << endl;
}
int _c1;
};

class D1 : virtual public B
{
public:
virtual void f1()
{
cout << "D1::f1()" << endl;
}
virtual void f2()
{
cout << "D1::f2()" << endl;
}
int _d1;
};

class E1 :public C1, public D1
{
public:
virtual void f1()
{
cout << "E1::f1()" << endl;
}
virtual void f2()
{
cout << "E1::f2()" << endl;
}
virtual void f3()
{
cout << "E1::f3()" << endl;
}
int _e1;
};

int main()
{
cout<<sizeof(A)<<endl;       //4
cout<<sizeof(B)<<endl;      // 8
cout<<sizeof(C)<<endl;     // 12
cout<<sizeof(E)<<endl;     //28
cout<<sizeof(E1)<<endl;   //32
E e;
e.D::_b = 1;
e.C::_b = 2;
e._c = 3;
e._d = 4;
e._e = 5;
E1 e1;
e1.D1::_b = 1;
e1.C1::_b = 2;
e1._c1 = 3;
e1._d1 = 4;
e1._e1 = 5;
return 0;
}


(1)A的大小为4个字节,因为在大部分的情况下计算类的对象大小时只需要计算它的成员变量的大小就可以了,而不需要计算成员函数的大小。
(2)B的大小为8个字节,因为B除了它本身的成员变量大小占4个字节之外,它还有一个虚函数,虚函数需要4个字节去存放虚函数表,所以B的大小为8个字节
(3)C的大小为12字节,是因为它继承了类B,类B的大小为8个字节,再加上它自己的成员变量4个字节,所以有12个字节,注意C的大小不是16个字节,因为子类虽然拥有自己的虚函数,但是与父类共用一张虚表。




(4)当存在菱形继承,且不存在虚继承的时候,类E的大小为28个字节,本身的4个int类型的成员变量占有16个字节,但由于菱形继承存在二义性和数据冗余,导致基类B的成员变量_b存在两个值,所以类E的数据成员变量为20个字节,再加上要存放两张虚表的地址,需要8个字节,所以需要类E的大小为28个字节。
菱形继承时对象e内存的内存布局:




(5)当存在菱形继承,且存在虚继承的时候,类E1的大小为32个字节,本身的4个int类型的成员变量占有16个字节,加上要存放两张虚基表的地址,需要8个字节,再加上被重写的虚函数表的地址,和未被重写重写的虚函数表的地址共8个字节,就来到了32个字节,假如E1类中没有虚函数f3(),那么E1类的大小为28个字节,因为它只要一张虚表就可以了,除了被重写的虚函数没有其他虚函数,那么它和父类共用一张虚函数表。
注:类的静态成员变量不计入类得大小,因为static 修饰的变量,是所有对象共享的,所以不属于某个对象,存在静态区。
菱形继承且为虚继承的对象e1的内存布局:




7.总结

计算各种数组时:

sizeof();当括号传数组名,指的是整个数组所有元素占多少个字节;当传数组元素,指的是数组元素占多少个字节;一般有取地址符&使用sizeof计算所占内存的大小即为计算地址的大小为四个字节(32位机器)。


计算类的大小时:

(1)首先看一下类有没有父类,如果没有父类:

a:且没有虚函数的话,直接计算类的成员变量的大小(注:类的静态成员变量不计入类的大小);
b:有虚函数的话,类的大小=类的成员变量的大小+一个虚表地址的大小  ;


(2) 有父类:

a:父类没有虚函数,子类也没有虚函数 ,直接计算两个类的成员变量的大小相加;
b:子类有虚函数或者父类有虚函数或者两个都有虚函数,类的大小=两个类的成员变量的大小之和+一个虚表地址的大小;


(3)菱形继承(没有虚继承):



a:都不存在虚函数,由于会存在数据的冗余,类D的大小直接计算所有的类的成员变量的大小相加之外,还要再多加一份类A的数据成员变量的大小;
b:只要当A类存在虚函数时,不管派生类是否存在虚函数,是否构成虚函数的重写,类D的大小=A类数据成员变量的大小的*2+剩下所有类的成员变量的大小相加+两张虚表地址的大小
c:当A类不存在虚函数,B类和C类都存在虚函数时,类D的大小计算同上b;剩下的情况,类D的大小=A类数据成员变量的大小的*2+剩下所有类的成员变量的大小相加+一张虚表地址的大小


(3)菱形继承(且为虚继承):

a:都不存在虚函数,由于存在虚基表,


类D的大小直接计算所有的类的成员变量的大小相加之外,还要再多加类B和类D的两张虚基表的地址的大小;

b:类A存在虚函数,但是类B,类C,类D的虚函数都重写的是类A的虚函数(不论其中有几个类重写了A类的虚函数),只要他们所拥有的虚函数是重写类A的,那么


类D的大小=所有的类的成员变量的大小之和+两张虚基表的地址的大小+一张虚表的地址的大小

c:类A存在虚函数,只需要观察类A与其他三个类的关系,有几个类存在类A中没有的虚函数,就加上多少张虚表的地址的大小。


类D的总大小=所有的类的成员变量的大小之和+两张虚基表的地址的大小+n*一张虚表的地址的大小。比如类B、类C、类D中存都存在类A没有的虚函数,那么就需要加三张虚表的地址的大小。

d:类A中不存在虚函数,只需观察B类C类是否存在虚函数,都存在虚函数


类D的总大小=所有的类的成员变量的大小之和+两张虚基表的地址的大小+两张虚表的地址的大小

类A中不存在虚函数,B类、C类或者B类、C类、D类只有一个类存在虚函数


类D的总大小=所有的类的成员变量的大小之和+两张虚基表的地址的大小+一张虚表的地址的大小

虚函数表的构成详解:

http://blog.csdn.net/skyroben/article/details/68192874
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息