🚀write in front🚀
📜所属专栏:c语言学习
🛰️博客主页:睿睿的博客主页
🛰️代码仓库:🎉VS2022_C语言仓库
🎡您的点赞、关注、收藏、评论,是对我最大的激励和支持!!!
关注我,关注我,关注我,你们将会看到更多的优质内容!!
文章目录
前言🤯
一:结构体🤩
1.1:结构的基础知识:
1.2:结构的声明:
1.3:特殊声明(匿名结构体):
1.4:结构的自引用:
1.5:结构体变量的定义和初始化:
1.5.1结构体变量的初始化:
1.5.2结构体嵌套结构体的初始化
1.6:<font color="red">结构体的内存对齐(超重点!!★★★):
1.6.1:结构体的数据在内存中如何存储的?
1.6.2:为什么存在内存对齐?
1.7:修改默认对齐数:
1.8:结构体传参:
二:位段
2.1:什么是位段?
2.2:位段的内存分配
2.3:验证vs2019上位段的内存分配和使用
2.4:位段的跨平台问题
2.5:位段的应用
总结:
前言🤯
今天分享的内容是自定义类型之一的结构体。C语言本身为我们提供了一些类型,比如 int、char、float等等,我们可以利用这些类型来定义一些比较简单的事物,那如果要定义一本书呢?C语言自身提供的这些类型,好像都无法精准的帮我们定义出一本书,一本书包含:书名、作者、出版社等主要信息。为此,C语言为我们提供了结构体这种自定义类型,我们可以根据自己的需求去定义结构体里的成员列表,用来描述不同类型行的事物。接下来就让我们一起来看看,结构体里都有哪些有趣的知识吧!
一:结构体🤩
1.1:结构的基础知识:
结构体是一些值的集合,这些值称为成员变量,结构的每一个成员可以是不同类型的变量
数组也是一些值的集合,但一个数组里面存的都是相同类型的元素
1.2:结构的声明:
struct tag//tag是标签 { member-list;//成员列表,可以是一个或者多个 }variable-list;//变量列表
//结构体的声明: struct student { char name[20]; int age; char sex[5]; float score; }s1,s2; //定义结构体变量s1、s2 //此处定义的结构体变量是全局的 struct student s3, s4; //定义结构体变量s3、s4 //此处定义的结构体变量等同于声明时定义,也是全局的 int main() { struct student s5, s6; //定义结构体变量s5、s6 //此处定义的结构体变量是局部的 return 0; }
1.3:特殊声明(匿名结构体):
今天我们关于声明部分要补充的,是关于结构体的不完全声明,即匿名结构体类型:
struct //没有声明结构体标签,即为匿名结构体类型 { char name[20]; int age; char sex[5]; float score; }student = { "Zhang",21,"Man",91.7 }; //匿名结构体类型必须在生声明的同时进行定义
我们把这种在声明时省略掉结构体标签的结构体称为匿名结构体类型,在使用这种方式进行声明时,由于没有声明结构体标签,导致一旦该结构体结束声明,将无法再次进行定义,所以对于该类型的结构体来说,就必须在声明结构体的同时进行定义(可以不初始化)。
而我们再来看下面这个例子:
//结构体类型1: struct { char name[20]; int age; char sex[5]; float score; }x; //结构体类型2: struct { char name[20]; int age; char sex[5]; float score; }*p;
在这个示例中,虽然两个结构体类型内的结构体成员完全一样,但因为两者都使用了匿名结构体的声明方式,编译器会把上面的两个声明当成完全不同的两个类型,于是在下面的代码中将被视为非法:
p = &x; //一种类型的指针指向另一种不同类型,将被视为非法
而上面进行完全声明的 “ 学生 ”的示例中的s1~s6这六个结构体变量因为声明时声明了结构体标签(tag),所以会被视为同一种类型进行处理和调用。
1.4:结构的自引用:
顾名思义,结构的自引用就是指结构体在自己的声明中引用了自己的一种声明方式。
我们在结构体中套用结构体是可行的,但是像链表这一类的要存放下一个元素消息的,可不能直接存储下一个元素的所有消息,自引用中又嵌套了对自身的引用,如此循环往复,非常耗内存。所以,我们用存放指针的形式减少内存的消耗。
正确的方式是这样的:
struct Node { int data; struct Node* next;//结构体的声明里面包含一个同类型的结构体指针 };
当我们在进行结构体变量的定义时同样进行了自引用,不同的是这一次我们使用了一个指针,指向了下一个结构体变量的空间,而在这次指向之后,指针指向的空间被固定,不再指向其它空间,如此就实现了真正的结构体自引用。
同时,我们还可以结合关键字 typedef 进行使用:
typedef struct Test { int data; struct Test* NEXT; //但在这里必须仍使用struct Test //在结构体声明结束后才会进行重命名 }Test; //使用tepydef关键字,将struct Test类型重命名为Test类型 //typedef struct Test Test;//第二种重命名方式 int main() { Test n; //经过重命名,在进行定义时可以直接使用重命名后的类型名进行定义 return 0; }
1.5:结构体变量的定义和初始化:
1.5.1结构体变量的初始化:
struct Point { int x; int y; }p1={0,0}; struct Point p2 = {1,2}; //初始化 int main() { struct Point p3 = {3,4};//初始化 return 0; }
1.5.2结构体嵌套结构体的初始化
struct Point { int x; int y; }p1={0,0}; struct Point p2 = {1,2}; //初始化 struct S { int num; char ch; struct Point p;//嵌套一个struct Point类型的结构体p float d; }; int main() { struct Point p3 = {3,4};//初始化 struct S s1 = { 20,'w',{1,2},3.14f };//结构体嵌套结构体的初始化 struct S s2 = { .ch = 'w',.d = 3.14f,.num = 20,.p.x = 1,.p.y = 2 };//乱序初始化 printf("%d %c %d %d %f\n", s.num, s.ch, s.p.x, s.p.y, s.d);//打印s printf("%d %c %d %d %f\n", s2.num, s2.ch, s2.p.x, s2.p.y, s2.d);//打印s2 return 0; }