write in front :
个人主页 : @啊森要自信的主页
✏️真正相信奇迹的家伙,本身和奇迹一样了不起啊!
欢迎大家关注点赞收藏⭐️留言>希望看完我的文章对你有小小的帮助,如有错误,可以指出,让我们一起探讨学习交流,一起加油鸭。
文章目录
- 前言
- 什么是结构体?
- 结构体类型的声明和创建
- 结构体变量的初始化
- 结构成员访问操作符
- 匿名结构体类型
- 结构的⾃引⽤
- 总结
前言
本小节,阿森继续和你一起学习什么是结构体?结构体类型的声明和创建,然后就是结构体的初始化,随即学习结构成员的访问操作符来更好的打印结构体的数据,当然还有匿名结构体类型,和结构的自引用。文章干货满满,接下来我们就学习一下结构体吧 !
什么是结构体?
结构体是一种用户定义的数据类型,它允许用户根据需要组合不同类型的变量。
struct Student {char name[20];int age;float score;};
结构体通过struct
关键字来定义,它允许将多个不同类型的数据元素组合在一起,这些数据元素称为结构体的成员。
结构体类型的声明和创建
- 结构体类型的声明
struct 结构体类型标签名 {成员声明1;成员声明2;...};//分号不能丢
例如:
struct Student//此时只是声明了 Student 类型,{//但还没有创建任何 Student 类型的变量。int id;char name[20];float score;};//分号不能丢
- 创建结构体类型的变量的语法如下:
struct 结构体类型名 变量名;
例如:
创建一个名为student1的学生结构体变量:struct Student student1;
也可以在声明结构体类型的同时创建变量:struct Stu{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号};struct Stu s1;struct Stu s2;
3.typedef关键字来为结构体类型定义别名,然后创建结构体变量:
// 首先定义一个结构体类型struct Student {int id;char name[20];};int main() {// 使用typedef为Student结构体类型定义一个别名StudentTypetypedef struct Student StudentType;// 使用原结构体类型定义变量struct Student st1;// 使用新的类型别名定义变量 StudentType st2;// 访问结构体成员st1.id = 1001;st2.id = 1002;return 0;}
typedef struct Student StudentType
可以理解使用typedef把 struct Student重新取了一个名字 StudentType ,此时这个 StudentTye 就是一个类型,比如:int a
;这个StudentTye
就相当于int
, StudentType st2 ;当然,这个 struct Student 也可以理解为 int ,所以也可以这么用 struct Student st1 ;
结构体变量的初始化
结构体变量的初始化主要有两种方式:
- 按照默认顺序初始化:
默认情况下,结构体成员的初始化顺序与它们在结构体定义中的顺序相同。
例如:
struct Stu{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号}p1;struct Stu s1 = { "asenyaozixin",11,"男","2023012018" };//定义结构体变量s2
- 指定顺序初始化:
可以通过在列表中指定成员名来指定成员的初始化顺序:
例如:
struct Stu{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号};struct Stu s2 = { .age = 66,.id = "2023001001",.name = "ahuibuyiban",.sex = "nv" };
完整示例且打印:
struct Stu{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号};int main(){struct Stu s1 = { "asenyaozixin",11,"nan","2023012018" };//按照默认顺序初始化struct Stu s2 = { .age = 66,.id = "2024001001",.name = "ahuibuyiban",.sex = "nv" };//指定顺序初始化printf("%s %d %s %s\n", s1.name, s1.age, s1.sex, s1.id);printf("%s %d %s %s\n", s2.name, s2.age, s2.sex, s2.id);return 0;}
打印结果:
结构成员访问操作符
结构成员访问操作符用于访问结构体中的成员变量。
结构体成员的直接访问
- 结构体成员的直接访问—-点操作符(.)
使⽤⽅式:结构体变量.成员名
使用点操作符可以访问结构的普通成员,例如:
struct Stu{char name[20];int age;float score;} s3 = { "熊大", 33, 66.0f }, s4 = {"熊二", 18, 100.0f};//全局变量int main(){struct Stu s1 = {"zhangsan", 20, 95.5f};//局部变量struct Stu s2 = {"lisi", 18, 87.5f};struct Stu s5 = {.score= 98.5f, .name="hehe", .age = 18};//. 结构成员访问操作符//结构体变量.成员名//printf("%s %d %f\n", s1.name, s1.age, s1.score);printf("%s %d %f\n", s2.name, s2.age, s2.score);printf("%s %d %f\n", s3.name, s3.age, s3.score);printf("%s %d %f\n", s4.name, s4.age, s4.score);}
输出:
- 结构体成员的间接访问—-箭头操作符(->)
使⽤⽅式:结构体指针->成员名
当结构体变量声明为结构体指针时,使用箭头操作符访问其成员:
struct Stu{char name[20];int age;float score;} s3 = { "熊大", 33, 66.0f }, s4 = {"熊二", 18, 100.0f};//全局变量int main(){struct Stu s1 = {"zhangsan", 20, 95.5f};//局部变量struct Stu s2 = {"lisi", 18, 87.5f};struct Stu s5 = {.score= 98.5f, .name="hehe", .age = 18};//结构体指针struct Stu* p1 = &s1;//取出s1的地址struct Stu* p2 = &s2;//取出s2的地址struct Stu* p3 = &s3;//取出s3的地址struct Stu* p4 = &s4;//取出s4的地址struct Stu* p5 = &s5;//取出s5的地址printf("%s %d %f\n", p1->name, p1->age, p1->score);printf("%s %d %f\n", p2->name, p2->age, p2->score);printf("%s %d %f\n", p3->name, p3->age, p3->score);printf("%s %d %f\n", p4->name, p4->age, p4->score);printf("%s %d %f\n", p5->name, p5->age, p5->score);//结构体指针->成员名return 0;}
输出:
匿名结构体类型
匿名结构体类型就是没有给结构体类型起名字的结构体类型。
匿名结构体的定义方式:
struct {成员1 数据类型;成员2 数据类型;...} 变量名1, 变量名2, ...;
例如:
struct{int a;char b;float c;} x;
匿名结构体的特点是:
不需要给结构体起名字,定义时不指定结构体名称。
只能在定义它的代码块内使用,不能在其他地方再次使用这个匿名结构体类型。
思考:下⾯的两个结构在声明的时候省略掉了结构体标签(tag
),然后主函数里的p = &x的代码合法吗” />struct{int a;char b;float c;} x;struct{int a;char b;float c;} *p;int main(){p = &x;//?代码合法吗?return 0;}
输出没问题但有警告:
警告:
编译器会把上⾯的两个声明当成完全不同的两个类型,所以是⾮法的。
匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使⽤⼀次。
结构的⾃引⽤
结构的自引用指的是结构体内部包含自己类型的指针成员,通过这个指针可以实现结构体之间的引用关系。
⾃引⽤⽅式:
struct Node {int data;struct Node *next; };// Node结构体包含一个指向Node结构体的指针next// 通过next可以实现链表节点之间的引用关系
- 思考1:
在结构中包含⼀个类型为该结构本⾝的成员是否可以呢?
⽐如,定义⼀个链表的节点:
struct Node{ int data; struct Node next;};
上述代码正确吗?如果正确,那 sizeof(struct Node) 是多少?
仔细分析,其实是不⾏的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的⼤⼩就会⽆穷的⼤,是不合理的。代码运行:
图解分析:
- 思考2:
在结构体⾃引⽤使⽤的过程中,夹杂了 typedef 对匿名结构体类型重命名,也容易引⼊问题,看看下⾯的代码,可⾏吗?
typedef struct {int data;//存放数据Node* next;//存放写一个节点的地址}Node;int main(){return 0;}
运行:
分析:
首先使用typedef
给前面匿名结构体起了别名Node
,还不是类型,但是在typedef
语句内,struct
定义部分还没有结束,所以在struct内部使用Node
声明next
时,Node类型还未通typedef
获得定义,仅仅是对匿名结构体的一个重命名,就提前使⽤Node
类型来创建成员变量。
解决⽅案如下:定义结构体不要使⽤匿名结构体了
如下:
先定义结构体:
struct Node {int data;struct Node* next; }
再使用typedef给它起别名:
typedef struct Node Node;
或者一步完成:
typedef struct Node{int data;struct Node* next;} Node;
总结
这次阿森和你一起学习结构体的结构体类型的声明和创建,初始化,访问操作符,这是结构体基础知识,但阿森会慢慢和你一起学习,从基础到进阶。感谢你的收看,如果文章有错误,可以指出,我不胜感激,让我们一起学习交流,如果文章可以给你一个小小帮助,可以给博主点一个小小的赞