总结常出现的求sizeof值的情况
以前对于C/C++求一些变量的sizeof值很是疑惑,现在经过一段时间的了解,大致有个了解,现在对一些普遍存在,经常要求sizeof值得情况做个总结,整理了很长时间哦!
1. 普通变量的sizeof
普通变量的sizeof值就等于其类型的大小,与变量赋值没有关系。
例如:int n = 100; sizeof(n) = 4;
double m =123.456 sizeof(m) = 8;
2. 指针变量的sizeof
指针变量的sizeof值与指针所指的对象没有任何关系,既然是存放地址的,那么它当然等于计算机内部地址总线的宽度。所以在32位计算机中,一个指针变量的sizeof是4(以字节为单位),在64位系统中指针变量的sizeof结果为8。
例如:char *pc = “abc”; sizeof(pc) = 4;
int *pi; sizeof(pi) = 4;
string *ps; sizeof(ps) = 4;
char** ppc = &pc; sizeof(ppc)= 4;
void (*pf) ();(函数指针) sizeof(pf) = 4;
3. 数组的sizeof
数组的sizeof值等于数组所占用的内存字节数。
(3.1)数组的长度=数组元素*类型大小
例如:char a1[] = “abc”; sizeof(a1) = 3*1+1=4,注意:字符串末尾还存在一个空字符‘\0’
int a2[3] = {1,2,3}; sizeof(a2) = 3*4=12
(3.2)数组元素个数=数组长度/类型大小
例如:int c2 = sizeof(a2) /sizeof(int) = 3;
(3.3)数组“传址”(数组为函数参数)
数组在作为函数参数时,数组是“传址”的,调用者只需将实参的地址传递过去,所以数组蜕变成指针类型。
例如:void foo1(char a3[3])
{
sizeof(a3) = 4;
}
void foo2(char a4[])
{
sizeof(a4) = 4;
}
4. 结构体的sizeof
在没有#pragma pack宏的情况下,遵循以下三条内存对齐规则
(1)数据成员对齐规则:结构(struct)里的数据成员,第一个数据成员放在offset=0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员(比如数组,结构体等)大小的整数倍开始。(int在32位机上为4字节,则要从4的整数倍地址开始存储)。
(2)结构体作为成员:如果一个结构体里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那么b应该从8的整数倍开始存储)。
(3)收尾工作:结构体的总大小,也就sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
例如:struct aa
{
int id; // offset=0,是4的倍数,不用扩大
double weight; // offset=0+4=4不是8的倍数,扩大成offset=8
float height; // offset=8+8=16是4的倍数,不用扩大
// offset=16+4=20不是8的倍数,扩大成offset=24
}AA; //sizeof(AA)=24;
struct bb
{
char name[2]; // offset=0,是2的倍数,不用扩大
int id; // offset=0+2=2,不是4的倍数,扩大成offset=4
double score; // offset=4+4=8,是8的倍数,不用扩大
short t; // offset=8+8=16,是2的倍数,不用扩大
AA a; // offset=16+2=18,不是24的倍数,扩大成offset=24
// offset=24+24=48,是24的倍数,不用扩大
}BB; // sizeof(BB)=48;
5. 类的sizeof
(5.1) 空类的sizeof是1。空类是指没有成员的类,类中的函数不占空间,除非是虚函数。
例如:classA
{
public:
A(){}
~A(){}
void fun(){}
}; // sizeof(A)=1;
(5.2) 若类中包含成员,则类对象的大小只包括其中非静态成员经过对齐所占的空间,对齐方式和结构体相同。
例如:class B
{
public:
int b;
float c;
char d;
}; // sizeof(B)=12;
class C
{
public:
static int a;
int b;
float c;
char d;
}; // sizeof(C)=12;
(5.3) 在类中包含虚函数,则无论有几个虚函数,类的大小=数据成员的大小+4。
(因为编译器会为虚函数生成一张虚函数表,来记录对应的函数地址,因此在内存中只需要保存一个指向虚函数表的指针即可,指针大小为4.)
例如:class D
{
public:
D(){}
~D(){}
int a;
virtual void f(int) {}
virtual void f(double) {}
}; // sizeof(D)=8;
(5.4) 对于子类,它的类大小=父类大小+它的成员大小。(遵循对齐规则)
例如:classA1
{
public:
inta;
private:
char b;
}; // sizeof(A1) = 8;
class A2 : public A1
{
public:
char b;
short a;
}; // 8+1+2=11,不是4的倍数,由于对齐原则,sizeof(A2)=12
(5.5) 对于子类和父类中都有虚函数(但是不同的虚函数)的情况,子类的大小是父类的成员加上自己成员的大小,对齐后,再加上4。
例如:classB1
{
public:
B1(){}
~B1(){}
int a;
virtual void f(int){}
virtual void f(double){}
};
class B2 : public B1
{
public:
B2() {}
int b;
virtual void g(int){}
}; // 4+4=8,是4的倍数,已经对齐,所有sizeof(B2)=8+4=12
(5.6) 对于虚继承的子类,它的类大小等于父类大小加上它自己成员的大小,再加上它自己一个指向父类的指针(大小为4),对齐后的sizeof。
例如:class a
{
private:
int x;
}; // sizeof(a) = 4
class b : virtual public a
{
private:
int y;
}; // sizeof(b)= 4+4+4=12,是4的倍数,满足对齐原则
class c : virtual public b
{
private:
int z;
}; // sizeof(c)= 4+4+4=12,是4的倍数,满足对齐原则
class d : public b, public c
{
private:
int m;
}; // sizeof(d)=sizeof(b)+sizeof(c)-b和c相同的部分(a的成员大小,4)+自己的成员大小(4)=24
(5.7) 对于既有虚继承又有虚函数的子类,其类的大小等于父类大小(包括虚拟表指针,4)加上它自己的大小(包括虚拟表指针,4),再加上它自己一个指向父类的指针(大小为4),对齐后的sizeof值。
例如:class B1
{
public:
B1(){}
~B1(){}
virtual void f(int){}
virtual void f(double){}
}; // sizeof(B1)=4
class B2 : virtual publicB1
{
public:
B2() {}
virtual void g(int){}
}; // sizeof(B2)=sizeof(B1)+4(自己的大小)+4(指向父类的指针)=12