目录
结构体
- 数组是一组相同元素的集合
- 而结构体则是一些值的集合,结构体的每个成员可以是不同类型
struct book
//struct 关键字,book 标签名,合起来叫做结构体类型
{
//成员列表,可以是多个不同类型的成员
char name[20];//书名
int price;//价格
char id[12];//出版社
}a1,a2,a3;//变量列表,全局变量
int main()
{
//局部变量
struct book b1;
struct book b2;
struct book b3;
return 0;
}
//匿名结构体,不完全声明
struct
{
char name[20];
int age;
char school[20];
}s;//结构体变量
//匿名结构体变量只能创建一次
struct A
{
int a;
int b;
};
struct B
{
int c;
struct A v;
//一个结构体可以包含另一个结构体
}
//错误自引用
//结构体自身大小未知
struct C
{
int n;
struct c d;
//无法确认结构体大小
//死递归
}
struct C
{
int n;
struct c *d;
//指向下一个结构体的指针
}
typedef struct Node
{
int data;
Node*next;
}Node;
//把struct Node类型重命名为Node
//但是在还没有声明之前就使用了它是不行的
typedef struct Node
{
int data;
struct Node *next;
//正确
}Node;
结构体的声明
struct A
{
cahr name[20];
int age;
int weight;
struct B school;
//嵌入调用
}struct A a={"zhangsan"};
//全局变量
struct B
{
char shool[20];
};
int main()
{
struct A a={18,130,{"jialidun"}};
//按顺序声明
struct A a={.age=18,.name="zhangsan",.weight=130,.B.shool"jialidun"};
printf("%s %d %d %s\n", A.name,A.age,A.weight,A.B.school);
return 0;
}
结构体的大小
规则
1.第一个成员对齐到结构体在内存中存放位置的零偏移处(占几字节给几字节)
2.第二个成员开始,都要对齐到一个对齐数的整数倍处
对齐数:结构体成员大小和默认对齐数的较小值
vs:默认对齐数是8
Linux gcc:没有对齐数,结构体成员自身大小
3.结构体的总大小,必须是所有成员的对齐数中最大对齐数的整数倍
4.如果结构体嵌套结构体成员,要将嵌套的结构体成员,对齐到自己的成员中最大对齐数的整数倍处
!结构体的总大小必须是最大对齐数的整数倍,包含嵌套结构体成员中的对齐数的所有对齐数
- 按照原来的理解方式
- c1占用1字节,c占用4字节,c2占用1字节,一共也就6字节
- 但是结构体却不是这样使用空间的
- offsetof宏计算结构体成员相较于起始位置的偏移量
- #include<stddef.h>
- offsetof(结构体类型,结构体变量)
- 为什么有对齐?
- 平台原因,性能原因
- 结构体的内存对齐是拿空间换时间的做法
- 尽量把占用空间小的成员放在一起
默认对齐数的修改
- #pragma comment()
- #pragma pack()//修改默认对齐数
#pragma pack(1)
//修改默认对齐数为1
struct A
{
char a;
int b;
char c;
};
#pragma pack()
//恢复默认对齐数
int main()
{
printf("%d\n",sizeof(z));
return 0;
}
结构体传参
结构体在传参时尽量使用传址调用
在传递一个结构体时,太大会在压栈时耗费过多的空间,造成性能的下降
位段
位段成员必须是int,unsigned int ,signed int,char类型
位段成员必须后面跟上 : 和数字
位段的空间是按照四字节(int)或一字节(char)的方式来开辟的
最大值为32bit
位段优点比结构体节省空间
但是不能跨平台使用
struct A
{
//开辟4字节32bit
int _a:2;
//_a占2个比特位
//32-2
int _b:5;
//_b占5个比特位
//30-5
int _c:10;
//_c占10个比特位
//25-10
int _d:30;
//_d占30个比特位
//空间不够,再开辟4字节
//但是到底是先把之前的15bit用掉,再用之后开辟的空间是不确定的
//所以位段有很多不确定性,不能跨平台使用
};
int main()
{
printf("%d\n",sizeof(struct A));
//8
return 0;
}
枚举
枚举的优点
增加代码的可读性和可维护性
和#define定义的标识符常量比枚举有类型检查,更加严谨
防止命名污染
便于调试
使用方便,可以定义多个常量
#define RED 1
#define
enum Cal
{
add,
sub,
mul,
div,
exit
};
void menu()
{
printf("***1.add***2.sub***\n");
printf("***3.mul***4.div***\n");
printf("***0.exit******\n");
}
int main()
{
int input =0;
do
{
menu();
scanf("%d",&input);
switch(input)
{
case add:
{
break;
}
case sub:
{
break;
}
case mul:
{
break;
}
case div:
{
break;
}
case exit:
{
break;
}
default :
{
break;
}
}
}while(input);
int a
return 0;
}
联合体 (共用体)
//联合体关键字 union
//多个成员共同享有一个空间
union Un//union Un类型
{
char c;//1
int i;//4
//共用体/联合体
//c,i共用一个空间
//联合体的大小至少是最大成员的大小
//不能一起使用同一片空间
};
int main()
{
union Un u;
//printf("%d\n",sizeof(u));//?
//打印为4
printf("%d\n",&u);
printf("%d\n",&(u.c));
printf("%d\n",&(u.i));
return 0;
}
//判断大小端
int Big()
{
//此联合体为判断大小端函数所创建
union Un
{
char i;
int n;
}u;
u.n = 1;
return u.i;
//返回1是小端,返回0是大端
}
int main()
{
if (Big())
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
//联合体大小计算
union Un
{
int i;//4,int 类型为 4
char n[5];//5,char 类型为 1
//最大对齐数为4,但是5却不是4的倍数所以空间增加到第八个空间
}
总结
联合体的大小至少是最大成员的大小
当最大成员大小不是最大对齐数的整数倍时,需要对齐到最大对齐数的整数倍