sizeof是运算符,可用于任何变量名、类型名或常量值,当用于变量名(不是数组名)或常量时,它不需要用圆括号。结果类型是size_t,它在头文件中typedef为unsigned int类型。主要有四种语法形式:
1. sizeof object
2. sizeof(object)
3. sizeof type_name
4. sizeof(type_name)
//自定义类型
class A
{};
//声明一个变量
int i;
A a;
//下面几种情况
sizeof i; // =4
sizeof(i); // =4
sizeof a; // =1
sizeof(a); // =1
sizeof int; // 错的
sizeof(int); // =4
sizeof string; // =32
sizeof(string); // =32
sizeof 1; // =4
sizeof 1>2?true:1.0; // 错的
sizeof(1>2?true:1.0); // =8
sizeof 2+3.1; // =7.1
它在编译时起作用,而不是运行时。
这里统一一下,在32位操作系统下,一个字为4个字节,char占1个字节,short占2个字节,int占4个字节,double占8个字节,string占32个字节,指针占4个字节。
// 1.只有一个 int 的结构体
struct S1
{
int a;
};
sizeof(S1)=4
// 2.有 int , char 两个属性的结构体
struct S2
{
int a;
char b;
};
sizeof(S2)=8
// 换一下位置
struct S3
{
char a;
int b;
};
sizeof(S3)=8
// 3.有 char , char , int 三个属性的结构体
struct S4
{
char a;
char b;
int c;
};
sizeof(S4)=8
// 换一下位置
struct S5
{
char a;
int b;
char c;
};
sizeof(S5)=12
// 5.包含其他结构体的结构体
struct S6
{
char a;
S5 b;
double c;
};
sizeof(S6)=24
// *6.包含指针的结构体
struct S7
{
char *a;
};
sizeof(S7)=4
sizeof(结构体)有三条规则: 1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。
根据我个人的理解,有下列几条:
1)
最宽基本类型成员,指的是占的字节最多的内置类型。S1-5中最宽基本类型都是int。再看S6中,在计算最宽基本类型的时候要分解S5,其中最宽的是int,比double小,所以取double,即8个字节作为单元。(注意string类型其实也是复合的,可以看成char数组)
2)
填充字节,我们可以用 _ 表示一个空的字节。填充字节会发生在三种地方,对应上述三条规则,等下分析。
3)
对结构体首地址的偏移量,可以用数格子的方法来判断,例如S3,a _ _ _ b b b b,b的偏移量就是4,;S5 ,a _ _ _ b b b b c _ _ _ ,c的偏移量就是8。
4) 分析下填充字节出现的三个场景:
1. 第一个“结构体变量的首地址能够被其最宽基本类型成员的大小所整除”,参考S6中的S5来看,如果首地址为0,那么a就占了0的位置,那么b呢?你会不会想到b占的是第8个位置,即a _ _ _ _ _ _ _ b ……。如果是这样那么可能你理解错一句话,“其最宽基本类型成员的大小”,这里指的是S5中最宽基本类型成员
int 而不是S6中的double,即a _ _ _ b ……。
这里就发生了符合规则一的填充字节。为了验证正确性,如下:
S6 s;
cout<<(long)&(s.a)<<endl; // 1767000 a的首地址
cout<<(long)&(s.b)<<endl; // 1767004 b的首地址
cout<<(long)&(s.c)<<endl; // 1767016 c的首地址
// a和b之间差了4个字节
2. 第二个“结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding)”,参考S5来看,a占0的位置,那么b呢?按照这个规定,应该是a
_ _ _ b b b b ……。如果这里将b从int 改为short,那么应该是a _ b b …… 。举例如下:
S5 s;
cout<<(long)&(s.a)<<endl; // 1833640 a的首地址
cout<<(long)&(s.b)<<endl; // 1833644 b的首地址
cout<<(long)&(s.c)<<endl; // 1833648 c的首地址
3. 第三个“结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)”,参考S5来看,我们之前分析了a
_ _ _ b b b b c ,那么一共需要9个字节,最宽基本类型成员是int,根据这个规则需要在末尾填充3个字节,即a _ _ _ b b b b c _ _ _ 。不举例了,看总大小就好。 试试下面的例子:
// 最宽基本类型成员为short ,大小为2
struct A1{
char a;
short b;
char c;
};
sizeof(A1)=6 //(a _ b b c _ )
// 最宽基本类型成员为short ,大小为2
struct A2
{
char a;
S5 b;
char c;
};
sizeof(A2)=10 //( a _ ( a _ b b c _ ) c _ )
// 最宽基本类型成员为double ,大小为8
struct A3
{
char a;
A1 b;
double c;
};
sizeof(A3)=16 //( a _ ( a _ b b c _ ) c _ _ _ _ _ _ _ )
现在来看下几个比较特殊的举例:
// *7.包含static的结构体
struct S7
{
char* a;
};
sizeof(S7)=4
// *8.包含static的结构体
struct S8
{
static int a;
};
sizeof(S8)=1
// *9.包含array的结构体
struct S9
{
char a[10];
int b;
};
sizeof(S9)=4
5) 参考S7,对于结构体内有指针的情况,无论指针是什么类型的,都是一个字的长度(因为他只是存储一个内存地址,跟具体类型没关系),对于32位系统来说就是4个字节(我记得是)。所以关于指针的大小可以类似参考int型。
6) 参考S8,对于含有static的结构体来说,sizeof是不算在大小里面的。我在网上看到一种说法,说sizeof只计算栈内的元素,而静态变量不计算在栈中,所以不计算在内。(空结构体的大小不是0,而是1,“空结构体”变量也得被存储,这样编译器也就只能为其分配一个字节的空间用于占位了。)
7) 参考S9,对于含有array数组的结构体来说,其最宽基本类型成员为数组的类型,大小为 number * sizeof( 数组类型 )。