一、基本内置类型
貌似没啥好说的,不过有些值和机器相关。最常见的是32位机器,对应如下:
char c;
short s;
int i;
long l;
float f;
double d;
long double ld;
cout << sizeof(c) << endl;
cout << sizeof(s) << endl;
cout << sizeof(i) << endl;
cout << sizeof(l) << endl;
cout << sizeof(f) << endl;
cout << sizeof(d) << endl;
cout << sizeof(ld) << endl;
结果:1 2 4 4 4 8 8
二、指针与引用
1、 这个的关键在于认出实参是个指针,只要是指针返回值都是4。
2、引用相对来说比较容易识别,实参通常都加&操作符,返回当然也是4。
3、要是有*操作符就要看指针指向的是个什么东西了。
char *c = "0123456789";
short s;
cout << sizeof(c) << endl;
cout << sizeof(*c) << endl;
cout << sizeof(&c) << endl;
cout << sizeof(&s) << endl;
结果:4 1 4 4
三、字符串与数组
1、长成这样的字符串char *c = "123"; 这TM就是唬人的,这传进去分明是个指针,结果都是4;
2、长成这样的字符串 string s = “123”; 这个更TM唬人,这传的是个string类型的对象,结果都是32;
3、c风格的字符串使用sizeof结果是实际字符数+1(因为结尾还有个\0) char c1[] = ""; 对应值1,char c1[] = "12\n"; 对应值4;
4、好吧char[] 本质来说是个数组,数组在没有给定长度也没有赋值初始化时是个不完全类型,没法用sizeof。
5、数组一旦给定长度后,sizeof结果是长度与数组类型所占字节的乘积;
//char c[];
char c1[] = "0123456789";
char c2[100];
char c3[100] = "0123456789";
int i[100];
string s;
string s1 = "0123456789";
string s2 = "0123456789012345678901234567890123456789";
//cout << sizeof(c) << endl;
cout << sizeof(c1) << endl;
cout << sizeof(c2) << endl;
cout << sizeof(c3) << endl;
cout << sizeof(i) << endl;
cout << sizeof(s) << endl;
cout << sizeof(s1) << endl;
cout << sizeof(s2) << endl;
结果:c是个不完全类型没法用sizeof所以注掉 c1开始 11 100 100 400 32 32 32
四、关于函数
1、sizeof不能直接用于函数类型,如int fun(){return 1;} sizeof( fun ); 是错误的;
2、sizeof参数可以是调用一个返回值不是void的函数 如 sizeof( fun() ); 结果取决于fun()的返回类型。void不能用于sizeof;
五、类和对象
1、没有任何成员的空类,sizeof返回它的大小依然是1;
2、对象大小和其类的大小相等;
3、数据成员遵循数据对齐:所需要内存总是类的数据成员中,最大成员大小的倍数(如果是数组,最大成员考虑的是数组类型,而不是整个数组所占的大小)。数据成员在类中声明的顺序直接影响对齐方式,从而影响类所需内存:
class A
{
int i;
char c;
double d;
};
class A1
{
int i;
double d;
char c;
};
分析:
在A类中,最大成员 d 占8字节,一行需要8字节;
按顺序对齐 i 4、c 1, 4+1<8,放在一行;
继续d占8, 4+1+8 > 8, d单独放一行,并将 i c所在行进行补齐到8字节;
结果为 16
在A1类中,最大成员 d 占8字节,一行需要8字节;
顺序对齐 i 4;
继续d 占 8, 4+8 > 8, d单独放一行,将i所在行进行补齐到8字节;
继续c占1,8+1 > 8, c只能也单独放一行,之后没有成员了,c所在行也需补齐到8;
结果为 24;
上述成员变量的声明顺序显然后者浪费内存,不过有的时候这样做能加快CPU的速度,比如存储字节为4的倍数比较有利CPU工作。在MS VC++中可以使用#pragma pack(n)来设置按制定字节进行对齐,或在选项中设置不进行数据对齐;
4、静态成员存放在全局数据区,而sizeof只计算栈内,所以不计算;
5、非virtual成员函数不计算在内,一旦成员函数中存在虚函数则需要再在最后加入一个隐含虚指针成员,同样也要注意内存对齐(指针在最后加入)。在继承关系中,如果基类和派生类中都有虚函数,则在计算派生类的时候只进行一次虚指针成员添加,派生类与它的基类共享同一虚函数表,虚指针不能算作两个:
class A_Base
{
public:
virtual ~A_Base(){}
};
class A:public A_Base
{
public:
virtual ~A() {}
};
上述 A类 与其基类 A_Base 大小都是 4;
6、上述5的本质是虚函数表问题,当发生虚继承的时候,也会产生虚函数表,派生类也会生成一个虚指针:
class A_Base
{
};
class A:public virtual A_Base
{
};
上述 A类大小为4.
7、多重继承中,派生类的大小为 (其所有基类非共享部分的大小) + (由于虚继承产生的各个共享部分的大小)。看着好纠结啊:
class A
{
int i;
int n;
int m;
};
class A2
{
int i;
};
class B:public virtual A, public A2
{
};
class B1:public virtual A
{
};
class C:public B, public B1
{
};
分析: A 大小12, A2 大小4。
B大小 A + A2 + B类的虚指针 = 20
B1大小 A + B1类的虚指针 = 16
C大小 part_1 = B非共享部分大小 + B1非共享部分大小 = (A2 + B类的虚指针) + (B1类的虚指针) = 12;
part_2 = 虚继承共享部分大小 = A = 12;
part_1 + part_2 = 24;
这种纠结关系算的我好想骂人~~~
现在看来知道为毛都喜欢问sizeof相关的问题了,这玩意实在可以考察很多东西,简单到各种内置类型大小,复杂到继承多态,确实是基础知识的很好检验方式。