sizeof()运算符用来求对象所占内存空间的大小。
1、常见数据类型所占空间大小
下面是一些常见数据类型所占的空间大小
类型 | 32位操作系统 | 64位操作系统 |
---|---|---|
char | 1 | 4 |
bool | 1 | 4 |
short | 2 | 2 |
int | 4 | 4 |
long | 4 | 4 |
double | 8 | 8 |
float | 4 | 4 |
* | 4 | 8 |
2、数组的sizeof()
一般情况下数组所占内存空间的大小 = 单个类型所占空间大小 * 数组长度,不会因为你数组内是否有东西而影响,如下:
int a1[5];
int a2[] = { 1,2,3,4,5};
cout << sizeof(a1) << endl;
cout << sizeof(a2) << endl;
我们知道int数据类型大小为4字节,所以上面两个数组的大小就为 4*5=20字节。
对于字符数组有个特殊的字符‘\0’空字符。有下面几种情况
//大括号字符个数小于字符数组长度,其余空元素会自动赋值为空字符
char str1[10] = { 'a', 'b', 'c' };
cout << sizeof(str1) << endl; //输出为10
//大括号字符个数等于字符数组长度,系统不会自动添加'\0',
char str2[3] = { 'a', 'b', 'c' };
cout << sizeof(str2) << endl; //输出为3
//这种以字符串的形式赋值的,系统会自动在最后面添加'\0'
char str3[] = "hello world!";
cout << sizeof(str3) << endl; //输出为13,最后还有一个我们看不见的'\0'
cout << strlen(str3) << endl; //输出为12,这个函数返回字符串长度有效长度,碰到第一个‘\0’就会停止计算
//下面这种情况会报错,会提示const char[13] 不能赋值给 const char[12]
//因为字符串最后都会有一个'\0',把12改成13编译才能通过
char str[12] = "hello world!";
3、结构体的sizeof()
结构体的大小绝大部分情况下不会直接等于各个成员大小的总和,编译器为了优化对结构体成员的访问总会在结构体中插入一些空白字节。
3.1、结构体内存对齐原则
- 结构体的第一个数据成员存放的地址为结构体变量偏移量为0的地址,也就是首地址
- 结构体变量中的每个成员相对于结构体首地址的偏移量,都是该成员基本数据类型所占字节的整数倍。如char可以放在任何位置,int只可以放在结构体变量偏移量为4的倍数的位置或者结构体的首地址(为第一个数据成员时)的位置上。(内部对齐)
- 结构体变量的总大小,为结构体变量中“最大基本数据类型成员所占字节数”的整数倍。如果结构体只有有int、char、short三种类型,其中int所占空间最大,为4个字节,最后这个结构体所占空间就一定是4的倍数(补齐)
- 结构体变量的首地址,必须是结构体变量中的“最大基本数据类型成员所占字节数”的整数倍。(整体对齐)
3.2、案例
struct text1
{
char a;
short b;
int c;
};
struct text2
{
char a;
int b;
short c;
};
cout << sizeof(text1) << endl; //结果为8
cout << sizeof(text2) << endl; //结果为12
对于text1,char a占1个字节,存放在偏移量为0的位置;short b占2个字节,因为要存放在2的整数倍的偏移量的位置,只能存放在偏移量为2、3两个字节位置,这样偏移量为1的地方就被空出来了;int c就可以存放在4、5、6、7;最后因为最大的数据类型是int,所以这个结构体所占空间就要为4的倍数,刚好0-7占了8个字节,只不过是空的。
结构体变量偏移量 | 存放的变量 |
0 | a |
1 | 空 |
2 | b |
3 | b |
4 | c |
5 | c |
6 | c |
7 | c |
同理,这里只用了10个字节,最后要补齐到12个字节,所以最后要再开辟两个字节的空间。
结构体变量偏移量 | 存放的变量 |
0 | a |
1 | 空 |
2 | 空 |
3 | 空 |
4 | b |
5 | b |
6 | b |
7 | b |
8 | c |
9 | c |
10 | 空 |
11 | 空 |
3.3、#pragma pack(N) 指定对齐值
结构体的对齐规则的第二点,char可以放在任何位置是因为char的自身对齐值为1,可以被其他数整除。int的自身对齐值为4,只可以放在被4整除的地方。
我们可以通过宏#pragma pack(N)来指定这个对齐值,这个N只能是2的幂次方(1、2、4、8......)。计算的时候会使用自身对齐值和指定对齐值的最小值来作为最终对齐值。即min{自身对齐值,指定对齐值}。这时整体对齐,字节大小是min{所有成员中自身对齐值最大的, 指定对齐值} 的整数倍
下面代码同样的两个结果体text3、text4设置不同的对齐值最后所占内存空间大小也不同。
#pragma pack(1)
typedef text3
{
char a;
int b;
short c;
};
#pragma pack(2)
typedef text4
{
char a;
int b;
short c;
};
cout << sizeof(text3) << endl;//结果为7
cout << sizeof(text4) << endl;//结果为8
text3把对齐值设置为了1,所以int和short的对齐值都变成了1,也都可以放在任何位置,也就是所有数据在内存中是连续存储的。最后占的内存就是1+4+2 = 7个字节。7也是1的整数倍满足整体对齐。
结构体变量偏移量 | 存放的变量 |
0 | a |
1 | b |
2 | b |
3 | b |
4 | b |
5 | c |
6 | c |
text4把对齐值设置为了2,所以int的对齐值就变成了2,short还是2。然后int就要从偏移量2开始。
结构体变量偏移量 | 存放的变量 |
0 | a |
1 | 空 |
2 | b |
3 | b |
4 | b |
5 | b |
6 | c |
7 | c |
3.4、内存对齐的用处
内存对齐是一种优化技术,它对数据在内存中的排列进行调整,以提高访问效率和性能。以下是内存对齐的几个用途:
-
提高访问效率:内存对齐可以使得结构体成员在内存中按照自然边界对齐,减少内存访问的次数和开销。当数据按照对齐要求存储时,CPU可以直接读取整个对齐的数据块,而不需要多次读取多个不对齐的字节。
-
避免总线错误:某些计算机体系结构要求特定数据类型必须位于特定地址上。如果没有正确对齐,可能会产生总线错误,导致程序崩溃或产生不可确定的行为。
-
节省空间:内存对齐可能会引入填充字节来满足对齐要求,从而增加结构体的实际大小。然而,通过对齐对结构体进行布局,可以避免由于对齐问题产生的额外空间浪费。
-
兼容不同体系结构:不同的计算机体系结构对内存对齐有不同要求。进行内存对齐可以提高代码的可移植性,使得代码在不同的体系结构上能够正确地运行。