内存空间运算符sizeof
每⼀种数据类型都有⾃⼰的⻓度,使⽤不同的数据类型,能够创建出⻓度不同的变量,变量⻓度的不
同,存储的数据范围就有所差异,存储单位是字节,一个字节为8个bit。
size_t sizeof( a variable or a type (including aggregate types) )
sizeof是⼀个关键字,也是操作符,专门是⽤来计算sizeof操作符数的类型⻓度的,单位是字节。
sizeof操作符的操作数可以是数据类型,也可是变量、字面值。
The sizeof keyword gives the amount of storage, in bytes, associated with a variable or a type (including aggregate types). This keyword returns a value of type size_t.
sizeof(类型);
sizeof 变量或字面值; //变量或字面值可以省略括号
如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
double a = 0;
printf("%zd\n", sizeof(int));
printf("%zd\n", sizeof(char*));
printf("%zd\n", sizeof a); //变量或字面值可以省略括号
return 0;
}
运行结果:
4
4
8
基本数据类型和指针类型
int :4个字节
char :1个字节
float :4个字节
double :8个字节
…
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
printf("sizeof(int):%zd\n", sizeof(int));
printf("sizeof(char):%zd\n", sizeof(char));
printf("sizeof(float):%zd\n", sizeof(float));
printf("sizeof(double):%zd\n", sizeof(double));
//在x64环境中,指针类型永远占8个字节;在x86环境中,指针类型永远占4个字节
//1个字节 = 8个bit
printf("sizeof(char*):%zd\n", sizeof(char*));
return 0;
}
运行结果:
sizeof(int):4
sizeof(char):1
sizeof(float):4
sizeof(double):8
sizeof(char*):4
构造数据类型
数组
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
//数组的存储空间 = 数组长度 * 数组所存放的数据类型的存储空间,单位是字节
int arr1[6] = { 0 };
printf("%zd = %zd * %zd\n", sizeof(arr1), 6, sizeof(int));
char* arr2[10] = { NULL };
printf("%zd = %zd * %zd\n", sizeof(arr2), 10, sizeof(char*));
return 0;
}
运行结果:
24 = 6 * 4
40 = 10 * 4
结构体
结构体的内存对齐
结构体对齐规则
- 结构体的第⼀个成员对齐到和结构体变量起始位置偏移量为0的地址处
- 其他成员变量要对齐到相应的对齐数的整数倍的地址处
- 结构体的总大小为最大对齐数的整数倍。其中,最大对齐数为每一个成员变量的对齐数中的最大值。
注:对齐数:成员变量的第一个字节的地址相对于结构体首成员的第一个字节的地址(偏移量为0的地址处)的偏移量,这个可以类比数组的下标。
如何知道每个成员的对齐数?
对齐数 = 该成员变量所对应的类型的大小 与 编译器默认的一个对齐数 中的一个较小值
注:VS编译器的默认对齐数是8;Linux环境下的gcc编译器没有默认的对齐数,则对齐数为该成员变量的大小。
对齐规则应用
抛砖:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
struct S1
{
char c1;
int i;
char c2;
};
struct S2
{
char c1;
char c2;
int i;
};
int main()
{
printf("%d\n", sizeof(struct S1));
printf("%d\n", sizeof(struct S2));
return 0;
}
运行结果:
12
8
奇了怪了,我滴乖乖,这两个结构体的成员变量的类型和数量不都是一样的吗?
- 为啥最后的输出结果表示两者所占的内存空间不同?
- char占1个字节,int占4个字节,那么结构体不是应该占6个字节吗?
引玉:
为啥要内存对齐?
大部分的参考资料都是这么说的
- 平台原因 (移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定
类型的数据,否则抛出硬件异常。 - 性能原因:
数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要
作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地
址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以
⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两
个8字节内存块中。
总的来说:结构体的内存对⻬是拿空间来换取时间的做法,所以说我们在设计结构体的时候要让占用空间小的成员尽量集中起来,减少空间的冗余。
如何修改对齐数?
预处理指令#pragma,可以修改编译器的默认对齐数
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#pragma pack(1)
struct S2
{
char c1;
char c2;
int i;
};
int main()
{
printf("%d\n", sizeof(struct S2));
return 0;
}
运行结果:
6
联合体
也称为共用体,顾名思义,联合体的内部成员共享同一块内存空间。
联合体的内存对齐规则:
- 联合体内任意成员变量对齐到和联合体变量起始位置偏移量为0的地址处
- 联合体的总大小为最大对齐数(即各成员变量类型所占空间的最大值)的整数倍。
如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
union Un1
{
char c[5];
int i;
};
union Un2
{
short c[7];
int i;
};
int main()
{
printf("%d\n", sizeof(union Un1));
printf("%d\n", sizeof(union Un2));
//通过查看在联合体变量Un1中数组变量c[5]和整型变量i的地址,发现两者地址一致
union Un1 uu;
printf("%p\n", &(uu.c[0]));
printf("%p\n", &(uu.i));
return 0;
}
运行结果:
8
16
0056F9D4
0056F9D4
解释:
枚举
“枚举类型的尺寸是以能够容纳最大枚举子的值的整数的尺寸”,同时在标准中也说明:“枚举类型中的枚举子的值必须要能够用一个int类型表述”,也就是说,枚举类型的尺寸不能够超过int类型的尺寸。
但是是不是必须和int类型具有相同的尺寸呢?
上面的标准已经说得很清楚了,只要能够容纳最大的枚举子的值的整数就可以了。
所以其实可以说一个枚举类型所占空间的大小即为一个常数所占内存空间的大小,即一个int型所占内存空间的大小。
如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
enum Color
{
RED,
GREEN,
BLUE
}color;
int main()
{
printf("%d\n", sizeof(color));
return 0;
}
运行结果:
4