目录
结构体
匿名结构体
结构体嵌套
结构体大小
设置默认对齐数
位段
位段的跨平台问题
Enum枚举
枚举的好处
union联合体
判断大小端
内存大小
前面我们讲了结构体的基本知识,还有一些知识并没有罗列完全,这篇将结构体剩余的一些小知识铺垫出来。
struct
{
char book_name[20];
char author[20];
int price;
char id[15];
}sb1, sb2;//匿名结构体类型
像这样的结构体称为匿名结构体,注意匿名结构体变量必须在结构体声明尾部创建。
注意匿名结构体的生命周期只有声明这一行,且不同匿名结构体的类型都是不同的。
可以看到,即使内部有相同类型也无法被编译器识别,且脱离结构体定义匿名结构体也是非法的。
结构体里能否嵌套一个结构体呢?答案是可以的,不过要注意不能自己嵌套自己。
struct Node
{
int data;
struct Node n;
};
sizeof(Node);//能否计算结构体大小呢?
正确做法是用一个指针去接收结构体的地址,这种结构就是我们数据结构中链表的结构。
struct Node
{
int data;
struct Node* next;
};
为什么类型相同的结构体大小不一样呢? 这里我们用到一个函数叫做offsetoff来计算结构体成员偏移量。
stddef.h
偏移规则:
- 第一个成员对齐到起始位置为0的偏移处
- 后续成员偏移量必须为某个对齐数的整数倍处。
- 对齐数:自身大小与默认对齐数的较小值(vs默认对齐数为8)
- LInux下不设默认对齐数(结构体自身大小为对齐数)
- 结构体内存大小必须为最大对齐数的整数倍
- 嵌套的结构体应对齐到自身最大对齐数的整数倍处,该结构体的最大对齐数也就是作为成员的一个对齐数。
这种对齐方式使得在访问结构体时更加高效,是一种空间换时间的做法。我们创建结构体的时候也可以考虑按占用空间数将成员从小到大排列。
我们可以使用这条指令来修改默认对齐数:
#pragma pack()
#pragma pack(2)
struct S1
{
char c1;//2
int i;//4
char c2;//2
};//8
struct S2
{
char c1;//1
char c2;//1
int i;//4
};//6
- 位段——就是二进制位
- 位段的成员可以是整形家族的任意成员(包括char)
- 位段的空间按照int(4字节),char(1字节)开辟的(避免将int和char混用)
- 位段是不跨平台的,注意可移植性的程序应避免使用位段
struct A
{
int _a : 2;
int _b : 5;
int _c : 10;
int _d : 30;
};
struct B
{
char _a : 3;
char _b : 4;
char _c : 5;
char _d : 4;
};
我们可以在结构体中用 :指定该数据所占的比特位,大大减少了对空间的利用。
1.int位段被当成有符号数或无符号数是不确定的。
2.当开辟的空间大于剩余空间(int or char)时,是利用还是舍去剩余空间是不确定的。
3.位段中空间(bit)是从左向右利用或从右向左利用是不确定的
4. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机 器会出问题。
列举出可能的取值,这些可能取值为一个枚举常量,默认从0开始,依次递增1
//声明枚举类型
enum color
{
BLACK = 10,
PINK, //11
BLUE, //12
YELLOW //13
};
enum color a = BLACK;//初始化枚举变量
int num = BLACK;//可当作常量使用
- 增加代码的可读性和可维护性
- 和#define定义的标识符比较枚举有类型检查,更加严谨。
- 防止了命名污染(封装)
- 便于调试
- 使用方便,一次可以定义多个常量
联合体是一种特殊的自定义类型,它所包含的成员变量共用一块地址空间。
int check_sys()
{
union location
{
char i;
int n;
}u;
u.n = 1;//01000000
return u.i;//01
}
int main()
{
if(check_sys() == 1)
printf("小端\n");
else
printf("大端\n");
}
联合体的内存大小至少为最大成员变量所占空间大小,这点很好理解,共用一块空间必须满足最大成员变量的空间大小。除此之外,它也遵守结构体大小为成员中最大对齐数的整数倍的规则。
例:
单个char所占1个字节,而int为4个字节,成员变量最大的是arr占5个字节,所以整个结构体的大小应为最大对齐数(int)的整数倍,也就是8。