欢迎各位点赞👍评论✍收藏⭐
目录
1. 结构体
1.1 结构体定义
结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量,如:
标量、数组、指针,甚⾄是其他结构体
1.2 结构体的声明
struct tag
{
member-list;
}variable-list;
例:描述一个学生
struct Stu
{
char name[20]; //名字
int age; //年龄
char sex[5]; //性别
char id[20]; //学号
};
1.3 结构体变量的定义和初始化
//代码1:变量的定义
struct Point
{
int x;
int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
//代码2:初始化。
struct Point p3 = {10, 20};
struct Stu //类型声明
{
char name[15];//名字
int age; //年龄
};
struct Stu s1 = {"zhangsan", 20};//初始化
struct Stu s2 = {.age=20, .name="lisi"};//指定顺序初始化
//代码3
struct Node
{
int data;
struct Point p;
struct Node* next;
}n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化
1.4 结构体的特殊声明->匿名声明
在声明结构的时候,可以不完全的声明
//匿名结构体类型
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20], *p;
两个结构在声明的时候省略掉了结构体标签(tag);
但是要注意:
对于上面两个,下面代码是不合法的
p = &x
因为:
- 编译器会把上⾯的两个声明当成完全不同的两个类型,所以是⾮法的
- 匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使⽤⼀次
1.5 结构体的自应用
在结构中包含⼀个类型为该结构本⾝的成员是否可以呢?
例:
struct Node
{
int data;
struct Node next;
};
仔细分析,其实是不⾏的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的大小【sizeof(struct Node)】就会⽆穷的⼤,是不合理的;
正确的方式:
struct Node
{
int data;
struct Node* next;
};
应采用结构体指针的方法来自应用;
2. 结构体内存对齐
我们已经掌握了结构体的基本使用了
现在我们深⼊讨论⼀个问题:计算结构体的大小
这也是⼀个特别热门的考点:【 结构体内存对齐】
2.1 结构体内存对齐规则
计算时我们要掌握规则
2.1.1 偏移量
练习:
//练习1
struct S1
{
char c1;
int i;
char c2;
};
printf("%d\n", sizeof(struct S1));
//练习2
struct S2
{
char c1;
char c2;
int i;
};
printf("%d\n", sizeof(struct S2));
//练习3
struct S3
{
double d;
char c;
int i;
};
printf("%d\n", sizeof(struct S3));
//练习4-结构体嵌套问题
struct S4
{
char c1;
struct S3 s3;
double d;
};
printf("%d\n", sizeof(struct S4));
结果:
//练习1
----> 12字节
//练习2
----> 8字节
//练习3
----> 16字节
//练习4
----> 32字节
2.2 为什么存在内存对齐
1. 平台不同情况:不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定 类型的数据,否则抛出硬件异常
2. 电脑性能:数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要 作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地 址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以 ⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两 个8字节内存块中
总归一点:用空间来换取时间
那如何做到节省空间?我们可以这样写:
让占用空间小的成员尽量集中在⼀起
struct S1
{
char c1;
int i;
char c2;
};
struct S2
{
char c1;
char c2;
int i;
};
S1 和 S2 类型的成员⼀模⼀样,但是 S1 和 S2 所占空间的大小有了⼀些区别
2.3 自定义修改对齐数
【#pragma】 这个预处理指令,可以改变编译器的默认对⻬数
#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1
struct S
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的对⻬数,还原为默认
结果:
6字节
结构体在对齐方式不合适的时候,我们可以自己更改默认对齐数
3. 结构体传参
struct S
{
int data[1000];
int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s)
{
printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
printf("%d\n", ps->num);
}
int main()
{
print1(s); //传结构体
print2(&s); //传地址
return 0;
}
上述函数print2更好,因为:
- 函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销
- 如果传递⼀个结构体对象的时候,结构体过⼤,参数压栈的的系统开销⽐较⼤,所以会导致性能的下降
总结:结构体传参的时候,要传结构体的地址
4. 联合体
4.1 联合体类型的声明
- 像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以不同的类型
- 但是编译器只为最⼤的成员分配⾜够的内存空间
- 联合体的特点是所有成员共⽤同⼀块内存空间
- 所以联合体也叫:【共⽤体】
- 给联合体其中⼀个成员赋值,其他成员的值也跟着变化
#include <stdio.h>
//联合类型的声明
union Un
{
char c;
int i;
};
int main()
{
//联合变量的定义
union Un un = {0};
//计算连个变量的⼤⼩
printf("%d\n", sizeof(un));
return 0;
}
结果:
4字节
为什么?
4.2 联合体的特点
联合的成员是共⽤同⼀块内存空间的,这样⼀个联合变量的大小,⾄少是最⼤成员的大小(因为联合⾄少得有能⼒保存最⼤的那个成员)
#include <stdio.h>
//联合类型的声明
union Un
{
char c;
int i;
};
int main()
{
//联合变量的定义
union Un un = {0};
// 下⾯输出的结果是⼀样的吗?
printf("%p\n", &(un.i));
printf("%p\n", &(un.c));
printf("%p\n", &un);
return 0;
}
结果:
三个地址⼀模⼀样,就说明了联合体共用一个空间;
4.3 相同成员变量结构体和联合体比较
//代码1
struct S
{
char c;
int i;
};
struct S s = {0};
//代码2
union Un
{
char c;
int i;
};
union Un un = {0};
它们在内存中储存:
4.4 联合体大小的计算
- 联合的大小⾄少是最⼤成员的大小
- 当最⼤成员大小不是最⼤对齐数的整数倍的时候,就要对齐到最⼤对齐数的整数倍
计算下面两个联合体大小?
//代码1
union Un1
{
char c[5];
int i;
};
//代码2
union Un2
{
short c[7];
int i;
};
结果:
//代码1
----> 8字节
//代码2
----> 16字节
4.5 联合体判断大小端
int check_sys()
{
union
{
int i;
char c;
}un;
un.i = 1;
return un.c;//返回1是⼩端,返回0是⼤端
}
5. 枚举
5.1 枚举类型的声明
枚举顾名思义就是⼀⼀列举
把可能的取值⼀⼀列举
⽐如我们现实⽣活中:
- 三月有多少天------可一一列举
- 一个星期有多少天------可一一列举
- ....
enum Day//星期
{
Mon,
Tues, //记住是逗号
Wed,
Thur,
Fri,
Sat,
Sun
};
以上定义的 【enum Day 】是枚举类型
{..} 中的内容是枚举类型的可能取值,也叫枚举常量
这些可能取值都是有值的,默认从0开始,依次递增1,当然在声明枚举类型的时候也可以赋初值
enum Day//星期
{
Mon=2,
Tues=4, //记住是逗号
Wed=6,
Thur,
Fri,
Sat,
Sun
};
5.2 枚举的优点
- 增加代码的可读性和可维护性
- 和#define定义的标识符⽐较枚举有类型检查,更加严谨
- 便于调试,预处理阶段会删除 #define 定义的符号
- 使⽤⽅便,⼀次可以定义多个常量
- 枚举常量是遵循作⽤域规则的,枚举声明在函数内,只能在函数内使⽤
5.3 枚举的使用
光的三原色:红 绿 蓝
enum Color//颜⾊
{
RED=1,
GREEN=2,
BLUE=4
};
enum Color clr = GREEN;//使⽤枚举常量给枚举变量赋值
6. 小结
以上就是关于自定义类型的内容了,具体还需宝子们去实践,如果觉得该博客对你有用的话,希望一键三连,点个关注不迷路,谢谢支持!