前言

本篇我将梳理我所学的结构体的知识分享给各位,欢迎一起学习。

本人目前学识尚浅,若发现错误恳求指正,我会尽快修改,这对你我都有帮助,非常感谢!

求个赞>_<!

目录

什么是结构体?

结构体有什么用?

怎么使用结构体?

结构体类型声明、变量的定义和初始化

结构体类型声明方式

接下来是变量的定义 – 两种定义方式

第一种 – 在声明结构体时定义变量

第二种 – 在主函数内定义变量

变量初始化

结构体成员的访问方式 – 同样有两种

成员运算符访问

指针运算符访问

结构体大小的计算(内存对齐)

结构体传参

结构体实现位段


什么是结构体?

结构体就是自定义的数据类型。(只需要知道是一种数据类型便够了)

结构体有什么用?

结构体能描述复杂的对象,比如一个学生,就有姓名、学号、班级、性别等复杂信息。单靠我们熟知的基本数据类型intfloatchar难以描述。所以我们可以自己定义一个结构体来描述学生,结构体内可以定义学生各种信息的成员。下面将用代码展示。

结构体的用处非常广泛,值得我们进行刨根问底式学习,目前我在学习的数据结构就经常需要使用结构体,要学好数据结构,扎实的结构体知识是前提。结构体还在在计算机网络中运用于实现位段。

怎么使用结构体?

结构体类型声明、变量的定义和初始化

结构体类型声明方式

struct 结构名{成员变量;};

结构体的关键字是struct , 语法上大括号与最后的分号是必须的。关键字后面加的是结构体的名字,假如我们要描述学生,那就可以写成student见名知意。也可以不写结构名,那就是匿名结构体,只能使用一次(匿名结构体我不细说,因为我也没怎么用过,只知道你下面想用一次,那么就可以不用起名字)

下面我们定义一个学生结构体类型,并声明其姓名、学号、班级、性别成员

//头文件struct student{char name[20];//名字int num;//学号int class;//班级char sex;//性别};//千万注意分号不能丢//主函数

声明位置与包含头文件一样,这里要注意的是-成员是声明不要给具体值初始化。这样声明就完成了,此时的struct student 就是类型名了跟int 一样,千万不要以为student是变量。

既然这个结构体是一个数据类型,那么就可以用它声明各种变量,例如数组、指针等。其定义语法与基本数据类型类似例如struct student s[10];

接下来是变量的定义 – 两种定义方式

第一种 – 在声明结构体时定义变量
struct student{char name[20];int num;int class;char sex;}s1,s2,s3;

这里的s1、s2、s3就是变量,可以表示第一、第二、第三个学生.。

这种定义方法要注意与用了typedef 修饰区分,如果用typedef,那分号处不再是变量而是struct student的新名字

typedef struct student{char name[20];int num;int class;char sex;}Stu;//Stu 不是变量,此时 Stu就等价于 struct student
第二种 – 在主函数内定义变量
struct student{char name[20];int num;int class;char sex;};int main(){struct student s1,s2,s3;return 0;}

不多说 理解struct student是一种类型,跟int一样这一步就无比清晰,还可创建结构体类型的数组等等,方法与其他类型一致。

变量初始化

struct student{char name[20];int num;int class;char sex;};int main(){struct student s1;//定义s1 = {"zhangsan",202311,1,'b'};//初始化struct student s2 ={"xiaohong",202310,3,'g'};//定义+初始化 /*Stu s3 = {"lisi",202312,2,'b'};如果用了typedef起了新名字Stu*/return 0;}

结构体成员的访问方式 – 同样有两种

成员运算符访问

成员运算符是 . 一个英文小数点,具体使用方法,上代码

#includestruct student{char name[20];int num;int class;char sex;}s1,s2,s3;int main(){//用成员运算符访问第一名学生具体成员并初始化与打印出来s1.name = "zhangsan";s1.sex = 'b';printf("name:%s\nsex:%c",s1.name,s2.sex);return 0;}

用上成员运算符之后,就可以对具体成员进行操作

指针运算符访问

指针运算符是 -> 一个减号和一个大于号的组合,使用在指针上,需要该指针指向一个结构体变量,上代码演示

#includestruct student{char name[20];int num;int class;char sex;}s1,s2,s3;int main(){//定义结构体指针指向s1struct student *ps1 = &s1;//使用指针运算符进行初始化与访问ps1 -> name = "zhangsan";ps2 -> sex = 'b';printf("name:%s\nsex:%c",ps1->name,ps2->sex);return 0;}

运用到指针运算符的情况是当你拿到的不是结构体本身,而是一个指向结构体的指针时。

结构体传参

结构体传参要传地址,我们知道函数形参是在栈区创建与销毁,如果把整个结构体传过去要占用栈区大量空间,而且压栈过程中也会消耗时间。

结构体大小的计算(内存对齐)

//头文件struct student{char name[20];//名字int num;//学号int class;//班级char sex;//性别};//千万注意分号不能丢//主函数

问 :上面我们定义的这一结构体类型所占内存空间是多少?

两个char型2字节,两个int型8字节,是不是sizeof(struct student)的结果为10呢?

是简单的累和吗?

答: sizeof(struct student)的结果是16!!!

计算其大小还需谈及对齐规则:

1 – 结构体第一个成员总是从偏移量为0处开始对齐

2 – 其他成员从偏移量为对齐数的整数倍处开始对齐

3 – 成员变量为一个结构体时,对齐数为该结构体内最大的对齐数

4 – 最终所占空间大小为最大对齐数成员的整数倍

对齐数对齐数为编译器默认对齐数与成员变量的类型本身字节数的较小量,vs默认为8,编译器默认对齐数可以通过#pragma pack(8) – 设置默认对齐数为8(使用方法如下)

//头文件#pragma pack(1)//设置默认对齐数为1struct student1{char name[20];//名字int num;//学号int class;//班级char sex;//性别};//千万注意分号不能丢//主函数

有了对齐规则我们来计算一下,下面两个结构体类型的大小

//头文件struct student1{char name[20];//名字int num;//学号int class;//班级char sex;//性别};//千万注意分号不能丢//主函数
//头文件struct student2{char name[20];//名字char sex;//性别int num;//学号int class;//班级};//千万注意分号不能丢//主函数

下面是计算过程

从结果可以看出,我们在定义结构体成员变量时尽量把占用空间小的数据集中在一起定义,有利于节省空间。

为什么有内存对齐规则?举个简单的例子

CPU在读取内存数据时是整块整块的拿,假如CPU一次拿8字节出来,如果我们有一个8字节大小的double类型数据没有对齐到8的倍数处,那么CPU就需要两次内存访问才能将这个double类型数据读出来(因为只访问一次不能完全读取出来,还有部分在下一个8字节代码块处)。

总而言之,结构体内存对齐就是用空间换取时间的做法。

结构体实现位段

这里稍作了解即可

位段指的就是分配具体到比特位的空间给变量,在计算机网络传输上常用。

位段的可移植性低,跨平台的代码要避免使用位段。

位段的代码与普通结构体十分相似,只不过多了冒号与数字。

//头文件struct student2{int a : 32;//32比特位=4字节,最大成员int b : 7;//7个比特位存放0到2的7次方减一的数};//千万注意分号不能丢//主函数

注意到位段成员不得超过对应类型的大小,像这里的a成员32个比特位刚好是4个字节达到最大。

如果我们要表示学生的分数0到100分 ,那么只需要7个比特位就可以表示0到127分,就像b成员一样,当我们有大量小数据需要存储时使用位段,这样能在一定程度上节省空间

这篇文章就先到此结束,感谢观看,有收获就点个赞吧>_<