关于sizeof运算符
注:参考自《王道程序员求职宝典》。
1. 关于sizeof
sizeof是一个单目运算符,并不是一个函数。sizeof用来计算操作数在内存中所占的字节数。sizeof的计算发生在编译时,所以它可以被当作常量表达式使用,需要注意的是,它会忽略括号内的各种运算,如sizeof(a++);
中的++就不会被执行。
sizeof的返回类型为size_t,它被定义为unsigned int 类型。
2. sizeof的使用形式
sizeof的使用形式有两种:1)sizeof(var)
;2)sizeof var
。
- 当var为变量时,1)和2)两种形式均可;
- 当var为数据类型时,只能用1)。
3. sizeof的求值结果
3.1 sizeof(内置变量)
在32位环境中,有
sizeof(int) : 4
sizeof(short) : 2
sizeof(long) : 4
sizeof(float) : 4
sizeof(double) : 8
sizeof(char) : 1
sizeof(p) : 4 (p为指针, 64位环境下为8)
3.2 sizeof(函数)
sizeof可以对一个函数调用求值,其结果为函数返回类型的大小。函数并不会被调用。
#include <iostream>
char foo()
{
std::cout << "func has been called." << std::endl;
return 'a';
}
int main()
{
std::cout << sizeof(foo()) << std::endl;
return 0;
}
运行结果为:1。未打印"func has been called."。
【注】在对函数求sizeof时,需要注意的是,函数名、不能确定类型的表达式以及位域成员不能被计算sizeof。
如:
int foo() {return 1;}
sizeof(foo);
错误。函数名不能被计算sizeof。
void foo() {}
sizeof(foo());
错误。不能确定类型的表达式 不能被计算sizeof。
struct S
{
unsigned int f1 : 1;
unsigned int f2 : 5;
unsigned int f3 : 12;
};
sizeof(S.f1);
错误。位域成员不能被计算sizeof。
3.3 sizeof(指针)
sizeof(指针)在32位系统下结果为4,在64位系统下结果为8。
int* p;
sizeof(p)为4。
3.4 sizeof(引用)
引用变量的sizeof等同于被引用对象的sizeof。
int a[6] = {0, 2, 4, 6, 8, 10};
int (&p)[6] = a;
sizeof(p)为24。
3.5 sizeof(汉字)
Windows环境下一个汉字占两个字节。Linux环境下当使用UTF-8编码,每个汉字用3个字节表示。
sizeof("Abc汉字")为8。
3.6 sizeof(数组)
sizeof直接计算数组所占的字节大小。
int a[10];
char b[] = "hello";
sizeof(a)为40, sizeof(b)为6。
【注】数组作为形参或者数组是动态分配时,数组名当作指针使用。
void fun1(char a1[3])
{
int c1 = sizeof(a1);//c1=4
}
void fun2(char a2[])
{
int c2 = sizeof(a2);//c2=4
}
int* b = new int[10];
sizeof(b)为4。
3.7 sizeof(struct)
struct的空间计算较为复杂,总体上遵循两个原则:
1)整体空间是占用空间最大的成员(的类型)所占字节数的整数倍,但在32位Linux+gcc环境下,若最大成员类型所占字节数超过4,如double是8,则整体空间是4的倍数即可。
2)数据对齐原则——内存按结构体成员的先后顺序排列,当排到该成员变量时,其前面已摆放的空间大小必须是该成员类型大小的整数倍,如果不够则补齐,依次向后类推,但在Linux+gcc环境下,若某成员类型所占字节数超过4,如double是8,则前面已摆放的空间大小是4的整数倍即可,不够则补齐。
struct S1
{
char a;
double b;
int c;
char d;
};
struct S2
{
char a;
char b;
int c;
double d;
};
sizeof(S1)为24,sizeof(S2)为16。
3.7.1 结构体中包含结构体
两个原则应修改为:
1)整体空间是子结构体与父结构体中占用空间最大的成员(的类型)所占字节数的整数倍;但在Linux+gcc环境下,若最大成员类型所占字节数超过4,如double是8,则整体空间是4的倍数即可。
2)数据对齐原则——父结构体内存按结构体成员的先后顺序排列,当排到子结构体成员时,其前面已摆放的空间大小必须是该子结构体成员中最大类型大小的整数倍,如果不够则补齐,依次向后类推。但在Linux+gcc环境下,若某成员类型所占字节数超过4,如double是8,则前面已摆放的空间大小是4的整数倍即可,不够则补齐。
struct S1
{
char c;
int i;
};
struct S2
{
char c1;
S1 s;
char c2;
};
sizeof(S2)为16。
3.7.2 结构体中包含数组
在结构体中,数组是按照单个变量一个一个进行摆放,而不是视为整体。
struct S
{
char a[8];
int b;
};
sizeof(S)为8。
3.7.3 结构体中含位域
前面有提到位域成员不能单独计算sizeof值,不过我们这里要讨论的是含有位域的结构体的sizeof值。
大致规则为:
1)如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,知道不能容纳为止;
2)如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量位其类型大小的整数倍;
3)如果相邻位域字段的类型不同,不同的编译器具有不同的实现方式,VC6采取不压缩方式,Dev-C++与gcc采取压缩方式;
4)如果相邻位域字段之间穿插着非位域字段,则不进行压缩;
5)整个结构体的总大小为最宽基本类型成员大小的整数倍。
struct S1
{
int f1 : 3;
char b;
char c;
};
struct S2
{
char f1 : 3;
char f2 : 4;
char f3 : 5;
};
struct S3
{
char f1 : 3;
char f2;
char f3 : 5;
};
sizeof(S1)在Windows+Dev-C++下为4,在Windows+VC6下为8。
sizeof(S2)为2,因为1byte=8bit,而f1+f2=7bit,f3+7 > 8bit,因此f3需要从下一个字节开始保存。
sizeof(S3)为3。
3.7.4 使用“#pragma pack”
#pragma pack(n)
:编译器将按照n个字节对齐;
#pragma pack()
:取消自定义字节对齐方式。
以上两种指令通常应搭配使用。
3.7.5 空结构体
“空结构体”(不含数据成员)的大小不为0,而是1。因为“空结构体”也需要被存储,以便当存在多个空结构体时便于区分和取地址,编译器就只能分配一个字节的空间存储。
3.8 sizeof(union)
union的sizeof即为每个成员sizeof值中的最大值。
struct S
{
double d;
};
union U
{
int i;
char c;
S s;
};
sizeof(U)为8。
3.9 sizeof(enum)
enum只是定义了一个常量集合,里面没有“元素”,而枚举类型是当作int类型存储的,故枚举类型的sizeof值都为4。
struct W1
{
enum week { mon, tue, wed, thu, fri, sat};
enum day {monring, noon, afternoon, night};
} w1;
struct W2
{
enum week { mon, tue, wed, thu, fri, sat} today;
enum day {monring, noon, afternoon, night} now;
} w2;
sizeof(w1)为1,因为W为空结构体。
sizeof(W1::week)为4,sizeof(W::day)为4。
sizeof(w2)为8。
4. sizeof和strlen的区别
我们知道C/C++中所有的字符串常量都会有编译器自动在末尾添加一个空字符’\0’。
- sizeof会将字符串末尾的’\0’计算在内。
- strlen计算字符串中的字符数量,以’\0’为结束标志,不会将’\0’计算在内。
若有
char ch1[] = {'C', '+', '+'};
char ch2[] = {'C', '+', '+', '\0'};
对于以上两个字符串分别求得sizeof(ch1)=3, sizeof(ch2)=4
,而strlen(ch1)结果未知, strlen(ch2)=3
。
【注】sizeof("\0")=2
。