面试题中经常遇到,定义一个类,或为空,或定义了几个内置类型的变量,或包含构造、析构函数、成员函数,问在32位/64位平台上,sizeof(class)的字节数。比较关键的几个考虑:
以下讨论,都是使用g++编译的情况,当使用gcc编译时,关于内存对齐,主要是两个原则:
class A
{
int a;
short b;
int c;
char d;
};
//使用`gcc`编译时,按照以上规则,a占4个字节,b本应占2个字节,
//但由于c占4个字节,为了满足条件2,b多占用2个字节,为了满足条件1,
//d占用4个字节,一共16个字节。
class B
{
double a;
short b;
int c;
char d;
}
//在windows下,无论使用gcc、g++,结果都是24;而Linux/Mac下,任何编译器,都是20;
//使用`gcc`编译时,a占8个字节,b占2个字节,但由于c占4个字节,
//为了满足条件2,b多占用2个字节,即abc共占用8+4+4=16个字节,
//为了满足条件1,d将占用8个字节,一共24个字节。
class A
{
};
//sizeof(A)为1
类的实例化,即在内存中分配一块地址,空类也会被实例化,因此编译器会为其隐含地添加一个Byte,空类实例化之后才能拥有独一无二的地址。
class A
{
int a;
char *p;
};
在32位平台,sizeof(A)
为8,int为4Byte,char指针亦为4Byte;若在class A里面再加一个char q,按照4字节对齐,即char q会占据4Byte,sizeof(A)为12;需要注意32位平台,sizeof(long)为4Byte,sizeof(long long)为8Byte;sizeof(void)为1,但是会报warning.
而对于64位平台,地址对齐跟编译器相关,比如对于class A里面添加一个long long c;
则sizeof(A)值为24;所以对齐方式,编译器占据决定性的作用;
存在编译器的编译优化问题,对于一个class,实例化以后,其分配的地址空间应该是连续的,对于8字节对齐的变量,比如double,char*,则按8字节对齐,对于int、char等,则按4字节对齐。
class A
{
public:
A(void);
virtual ~A(void);
private:
char a;
char b;
double p;
char *p;
};
//在32位平台,sizeof(A)为20,即4+4+8+4;而在64位系统,sizeof(A)为32;即
//8+4+8+8,凑够8的倍数,为32;
class A
{
public:
A(void);
virtual ~A(void);
private:
char a;
double p;
char b;
char *p;
};
//在32位平台,sizeof(A)为24,即4+4+8+4+4;而在64位系统,sizeof(A)为40;即8+8+8+8+8;
class A
{
public:
A(void);
virtual ~A(void);
private:
int a;
char *p
};
在32位平台,sizeof(A)
为12,有虚函数的时候,该类型会生成一个虚函数表,并在该类型的每一个实例中添加一个指向需函数表的指针,在32位系统分配的指针大小为4Byte.
class B : public A
{
public:
B(void);
virtual ~B(void);
private:
int b;
};
在32位平台,sizeof(B)
为16,即子类的大小为基类成员变量大小加上子类的大小。对于普通继承,子类和基类共享虚函数指针。
class A
{
int a;
}
class B : public virtual A
{
int b;
virtual void foo(){};
};
虚继承时,派生类会生成一个指向虚基类表的指针,如果还有虚函数,不增加额外指针大小空间。在32位平台上,类B大小为12Byte,由于virtual的存在,额外增加一个4Byte的指针大小。