🚠订阅专栏:自定义类型
🚜关注博主:翻斗花园第一代码手牛爷爷
🚙gitee仓库:牛爷爷爱写代码
目录
🚗前言
C语言给我们提供了很多基本类型,类如
int
,
char
,
float
等等类型,但是在生活中,我们往往需要去描述一个复杂的对象,例如一个人,一个物,单单一种属性是不能够描述一个复杂对象的,因此C语言提供给我们一种能够自定义的类型——结构体,本篇文章我们就来介绍结构体。
🚗结构体的声明
🚕结构体类型声明
结构体的声明非常简单,例如,当我们需要描述一本书的时候,书的描述就有书名,作者,定价,编号等等。因此我们就需要用到结构体类型,如下代码。
structbook{char book_name[20];char author[20];int price;char num[15];};
该代码中我们进行了结构体类型的声明,结构体类型为
struct book
,里面包括了书名,作者,定价,编号。
🚖定义结构体变量
在声明了结构体类型后,我们就可以定义结构体变量了。我们有两种方式对结构体变量进行定义,第一种方式是我们在结构体声明的时候对结构体变量进行定义,如下代码。
structbook{char book_name[20];char author[20];int price;char num[15];}book_1, book_2;
代码中我们定义了两个变量(两本书),分别为
book_1
和
book_2
。另外一种方式是在主函数或者是需要的时侯我们再进行变量的定义,如下代码。
structbook{char book_name[20];char author[20];int price;char num[15];};intmain(){structbook book_3;structbook book_4;return0;}
该代码中我们也定义了两个变量(两本书),分别为
book_3
和
book_4
,两种方式都可以进行结构体变量的定义,但是不同的是
book_1
和
book_2
是全局变量,
book_3
和
book_4
是局部变量。
🚌匿名结构体声明
我们在对结构体声明的时候可以省略结构体的名称,如下代码。
struct{char book_name[20];char author[20];int price;char num[15];}book_1, book_2;struct{char book_name[20];char author[20];int price;char num[15];}book_3, book_4;
代码中我们可以省略名称
book
,例如这种声明叫做匿名结构体声明,像是这种结构体的声明在创建变量的时候我们只能在结构体声明的时候创建变量,不能够在之后创建变量,如
struct book_1
,这种创建是错误的。注意:在创建两个匿名结构体类型的时候,哪怕两个匿名结构体里面的成员完全相同,但是在编译器看来这两个匿名结构体类型还是两个不同的类型,如上面代码中
book_1
和
book_2
就与
book_3
和
book_4
类型不同。
🚍结构体自引用
结构体的自引用我们经常在链表的使用中用到,它的变量结构通常分为数据域和指针域,我们一般把这样的一个变量叫做节点,如下图。
代码如下:
structnode{int data;structnode* next;};intmain(){structnode book_1;return0;}
当我们创建多个节点时,我们就可以利用指针域将多个节点串联起来,我们只需要知道第一个节点的起始地址就可以找到所有的节点,如下图。
🚓结构体的打印
结构体的打印分两种情况,第一种是利用结构体变量进行打印时,我们需要用到
.
操作符,如下代码。
structbook{char book_name[20];char author[20];int price;char num[15];};intmain(){structbook book_1 ={"高质量C/C++编程","林锐",99,"HX15935"};printf("%s %s %d %s\n", book_1.book_name, book_1.author, book_1.price, book_1.num);return0;}
我们在创建一个变量的同时对其进行初始化然后打印,打印结果如下图。
第二种是利用结构体指针进行打印,我们需要用到
->
操作符,如下代码。
structbook{char book_name[20];char author[20];int price;char num[15];};intmain(){structbook book_1 ={"高质量C/C++编程","林锐",99,"HX15935"};structbook* p =&book_1;printf("%s %s %d %s\n", p->book_name, p->author, p->price, p->num);return0;}
我们创建一个结构体指针指向结构体变量,然后打印,打印结果如下。
🚒结构体内存对齐
我们知道一个
int
类型占4个字节,一个
char
占1个字节,那么我们自定义的结构体类型又占多少个字节呢?如下代码。
structbook1{char x;int y;char z;};structbook2{char x;char z;int y;};intmain(){printf("%d\n",sizeof(structbook1));printf("%d\n",sizeof(structbook2));return0;}
我们定义了两个结构体,并且结构体成员都一样,只不过成员的存放顺序不一样,我们用
sizeof
来看一下两个结构体的大小有什么区别,运行结果如下。
通过上图我们可以看到,两个结构体的大小分别为12个字节和8个字节,为什么会产生这种结果呢?那是因为结构体内存要对齐,接下来我们来介绍结构体内存对齐的规则。
🚑结构体内存对齐规则
- 结构体第一个成员直接对齐到相对于结构体变量起始位置为0的偏移处。
- 从第二个成员开始,要对齐到某个【对齐数】的整数倍的偏移处。(对齐数:结构体成员自身大小和默认对齐数的较小值。vs环境下默认对齐数为8)
- 结构体总大小必须是最大对齐数的整数倍。(最大对齐数:每个结构体成员都有一个对齐数,其中最大的对齐数就是最大对齐数)
- 如果有嵌套了结构体的情况,那么嵌套的结构体要对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
🚐代码分析——book1
在了解完内存对齐的规则之后,我们再来分析上面的代码为什么会是不同结果,我们画图来分析,如下图。
变量
x
的直接存放到结构体变量开辟的空间的起始位置,类型为
char
占一个字节。然后放变量
y
,因为从第二个表变量开始,就要存放到对齐数的整数倍偏移处,对齐数为4。因此从偏移量为4的位置处开始存放,
y
的类型为
int
,占4个字节。最后存放变量
Z
,此时的对齐数为1,直接在
y
的后面进行存放,类型为
char
,占一个字节。又因为结构体总大小为最大对齐数的整数倍,最大对齐数为4,所以继续往后占用内存,直到偏移量为11,其中红色的都为浪费的字节,此时结构体大小为12字节。
🚚代码分析——book2
我们再来画图分析
book2
,如下图。
变量
x
直接存放偏移量为0的位置处,占一个字节。变量
z
的对齐数为1,直接存放到
x
的后面,占一个字节。变量
y
的对齐数为4,因此从偏移量为4的位置处开始存放,占4个字节,结构体大小为8。红色的都为浪费的字节。
🚋结构体嵌套
当我们一个结构体嵌套另一个结构体的时候,这时候大小又是多少呢?如下代码。
structbook1{char x;int y;char z;};structbook2{char x;structbook1 z;int y;};
此时的
book2
的大小为多少字节呢?我们还是画图来分析,如下图。
我们知道变量
x
直接存放变量起始位置,占一个字节,嵌套的结构体的要对齐到自己的最大对齐数的整数倍处,而
book1
的最大对齐数为4,因此从偏移量为4的位置处开始存放,
book1
占12个字节。变量
y
的对齐数为4,刚好从
book1
的后面,偏移量为16的位置处开始存放,占4个字节,结构体的总大小为20个字节。红色的为浪费的字节。我们来看一下运行结果,如下图。
🚉总结
本片文章介绍了结构体的内容,后续会继续更新自定义类型专栏,大家觉得有帮助的话可以点点订阅。由于作者水平有限,如果发现内容有误,还希望大家多多指正。如果有什么问题的话,欢迎大家评论区留言评论。
版权归原作者 翻斗花园第一代码手牛爷爷 所有, 如有侵权,请联系我们删除。