这是一个结构体,
test
是结构体标签,
c,s,i
是三种不同类型的结构体成员,
Test1
是结构体变量列表,假若这个结构体在main函数外定义,那么这个结构体的变量列表中的变量就是
全局结构体变量
。
结构体(struct)是由一系列具有相同类型或不同类型的数据构成的
数据集合
。
结构体的作用
在实际项目中,结构体是大量存在的。结构体和其他类型数据类型
大同小异
,只不过结构体可以做成自己想要的数据类型。
能够
被封装
是结构体的优点。封装的好处就是可以再次利用。让后续使用者只需根据定义使用就可以了。
内存对齐
那么结构体是怎样存储的呢?这涉及到一个规则:内存对齐。
struct test
{
char c;
short s;
int i;
}Test1;
什么是对齐?
计算机中内存都是按照字节划分的,但实际情况是在访问特定变量的时候通常需要在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
为什么要内存对齐
-
平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些
特定类型
的数据。各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取
效率
上带来损失。 -
性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问,所以内存对齐一方面是用内存空间换取时间效率。
在这介绍一个宏:
offsetof
这个宏用于计算结构体内变量相较结构体初始地址的
偏移量
,返回的是无符号整型。
结构体变量成员在内存中的存储位置如下图
图中黑色斜线表示被
空出来
的空间。
内存对齐细则介绍
- 结构体的第一个变量成员存放在结构体变量开始的0偏移量处。
-
第二个成员开始,每个成员都要对齐到
对齐数的整数倍
为下标的内存字节处,对齐数是成员自身大小和默认对齐数中的
较小值
。
默认对齐数
VS环境下默认为8。 -
结构体的
总大小
必须是最大的对齐数的整数倍,即是所有成员中对齐数
最大
的一个,不包括默认对齐数。 -
如果
嵌套了结构体
,嵌套的结构体要对齐到
自身(嵌套的这个结构体)中的变量的最大对齐数
的整数倍处,结构体的整体大小就是
所有最大对齐数
(
包括嵌套结构体的对齐数
)的整数倍。 -
对齐数就是成员自身的大小,即当前成员与默认对齐数中的
较小值
。并且默认对齐数也是可以被修改的,可以通过预编译命令
#pragma pack(n),n=1或2的倍数
来改变这一系数,其中的n就是”
对齐系数
“。
接下来再根据这些规则解释上面那个图
- 首先C会存储在结构体的零偏移量处,占一个字节。
- 其次short的大小是2个字节,而默认对齐数是8个字节,2<8,因此short的对齐数是2,S要存放在以2为倍数的下标的内存字节上,因此从2开始到3结束。
- 最后是I,int的大小是4个字节,4<8,因此int的对齐数是4,I要存放在以4为倍数的下标的内存字节上,因此I从4开始到7结束。
- 而结构体的总大小必须是最大的对齐数的整数倍,这三个变量中的最大对齐数是4,0~7一共8个字节,因此该结构体的内存分配在下标7处结束,得出该结构体的内存大小是8个字节。
结构体设计注意事项
看上图的这个结构体,因为double的大小是8个字节,所以每次存储都要在8的倍数为下标的的字节处,这样会导致白色的字节空间
被空出导致浪费
。因此在设计结构体的时候需要注意:
所有设计结构体的时候,如果成员的顺序允许被调整,可以把空间占用小的成员集中在一起定义,在结构体中的位置没有要求,这样某些情况下可以节省空间。
(本篇完)