结构体
结构体是一些值的集合,这些值被称为成员变量,结构的每个成员可以是不同类型的变量。
结构体的声明
1 | struct piont |
结构体在声明的时候可以省略结构体标签(tag),称之为匿名结构体,但很少会用到。
结构体可以使用 \{ \}初始化,但不能用它赋值。
结构体的成员访问
结构体变量访问成员,通过(.)操作符来访问。
例如:
1 | p1.x = 20; |
结构体指针访问指向变量的成员,使用( -> )操作符来访问
例如:
1 | printf("%d,%d\n", p4->x, p4->y); |
结构体可以自引用,但是只能通过包含自身类型的指针这种方式来自引用。
结构体的内存对齐
结构体的内存对齐规则:
- 第一个成员在与结构体变量偏移量为 0 的地址处。
- 其他成员变量要对齐到某个数字(称之为对齐数)的整数倍地址处。
- 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
对齐数: 编译器默认的一个对齐数,与该成员大小的 较小值
- VS中默认的对齐数值为8。
- Linux没有默认对齐数,它的默认对齐数等于当前字段的sizeof值。
- 可以用 #pragma pack ( )来修改默认对齐数,如果括号内不填写数字,则 还原成默认对齐数。
_举例一:_
(VS2013下)
1 | struct s{ |
对于该结构体类型来说,它占据内存空间大小的计算方法是:
- 根据第一条规则,对第一个成员变量,它在与结构体变量偏移量为0的地址处,此时算上一个字节。
- 根据第二条规则,对第二个成员,它是int类型变量,4个字节,与最大对齐数8相比,4更小,所以该成员的对齐数是4,要放在4的整数倍地址处,此时第一个成员占据的第一个字节之后的三个字节被开辟,在距离结构体变量偏移为4的地址处算四个字节,此时结构体变量有八个字节。
前两个成员
- 根据第二条规则,对于第三个成员,double类型,八个字节,与默认对齐数相等,所以对齐数取8,它放在8的整数倍地址上,即放在距离结构体变量偏移为8的地址处,此时结构体变量有16个字节。
前三个成员
- 根据第二条规则,第四个成员,char类型,一个字节,同上,它的对齐数是 1,所以直接放置即可,此时结构体变量有17个字节
- 根据第二条规则,第五个成员,类型是short,两个字节,同上,它的对齐数是2,放在地址是二的整数倍处,即第18个字节处,此时结构体变量为20个字节
所有的五个成员
- 根据第三条规则:结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍,这个结构体的最大对齐数是8,那么它占据的内存应该是八的整数倍,此时20个字节,补齐到24个字节即为八的整数倍
所以,在举例一种,该结构体的大小为24个字节。
_举例二:_
求此时struct s2 结构体类型的大小
1 | struct s{ |
- 根据第一条规则,对第一个成员变量,它在与结构体变量偏移量为0的地址处,此时算上一个字节。
- 根据第四条规则,如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍,结构体s中的最大对齐数是8,那么就要对齐到八的整数倍地址处,此时第一个字节往后开辟七个字节之后,来存放该结构体类型变量,这个结构体类型变量的大小在举例一中计算过,24个字节,所以此时s2为32个字节。
前两个成员
- 根据第二条规则,第三成员是char类型,1个字节,对齐数为1,所以此时结构体s2为33个字节。
- 根据第二条规则,第四成员为int类型,四个字节,对齐数为4,对齐到4的整数倍地址处,即第36个字节处,此时结构体大小为40字节。
- 根据第三条规则,该结构体各个成员中最大对齐数是八,所以它占据的内存应为八的整数倍,而40正好是八的整数倍,所以不用补齐
所以举例二中结构体类型的大小应为40字节。
位段
位段的声明和结构体类似,但是位段的成员必须是int 、unsigned int或signed int,位段的成员后有一个冒号和一个数字。冒号后的数字是几,就说明前边的成员变量占几个比特位。
1 | struct A |
枚举
将可能的取值一一列举,即为枚举
1 | enum season |
枚举常量的值默认由0开始依次递增,除非在声明中赋值
联合
该类型定义的变量包含一系列成员,但是这些成员都公用同一块内存空间
1 | union Un |
可以用它的特性来判断大小端字节序,例如:
1 | union Un |
联合的大小:
- 联合的大小至少是最大成员的大小
- 当最大成员的大小不是最大对齐数的整数倍时,要对齐到最大对齐数的整数倍上。