在学习数据结构的时候,会经常使用到结构体。今天分享的内容是结构体与指针,因为结构体和指针本身的内容并不是太多,所以今天的内容还包括了链表的实现。希望可以通过这篇博客,让大家熟悉结构体与指针,以及链表的实现。

一、结构体指针
1.1、结构体的简单介绍

在实际应用过程中,一组单一数据类型的数组很难满足我们的需求,这个时候就需要借助结构体。结构体是一种构造数据类型,它有许多不同数据类型的成员。假如我们需要整理一个班级学生的数据,具体要整理的数据如下:

  • 00后的人数
  • 女生的人生
  • 平均分数
  • 排名前五的学生的姓名

面对这样一个数据,无法用基本数据类型的任何一个表示。但是我们知道,要整理上面的数据需要知道下面几个信息:

  • 生日
  • 性别
  • 分数
  • 姓名

于是我们可以根据这些信息,创建一个结构体。结构体的创建格式如下:

struct 结构体名称{
	成员1
	成员2
	.....
}

定义一个结构体的关键词是struct,那么学生结构体的创建如下:

struct student{
	char name[20];
	char sex[10];
	int year;
	float score;
}

1.2、结构体变量和结构体数组

结构体和普通变量有很多类似的地方,我们可以将结构体理解为一个数组,一个存储不同数据类型的数组(只是作为理解,结构体和数组的操作完全不同)。

(1)结构体变量

结构体变量的声明和其它变量的声明类似,大概样式如下:

struct 结构体名称 结构体变量;

那么学生结构体变量声明如下:

struct student stu1, stu2;

另外我们可以用typedef关键字来简化结构体变量的声明,具体操作如下:

//将类型struct student 定义为 Student,以后看到了Student就可以用struct student替换
typedef struct student Student;

//那么学生结构体的定义可以变成如下
Student stu1, stu2;

(2)初始化结构体变量

在结构体中,我们可以通过“.”来操作结构体的成员:

Student stu1;
stu1.name = "zack";
stu1.sex = "male";
stu1.year = 1999;
stu1.score = 89;

因为结构体是构造数据类型,它所占的字节数是由每个成员占的字节数叠加的。

(3)结构体数组

结构体数组的定义和普通数组一致,我们可以循环初始化:

int i;
Student stu[3];
//循环输入数据
for(i = 0; i < 3; i++){
	printf("请输入姓名:");
	scanf("%s", stu[i].name);
	printf("请输入性别:");
	scanf("%s", stu[i].sex);
	printf("请输入年龄:");
	scanf("%d", &stu[i].age);
	printf("请输入分数");
	scanf("%f", &stu[i].score);
}
//循环输出结构体数组中的数据
for(i = 0; i < 3; i++){
	printf("*************************************");
	printf("姓名:%s\n", stu[i].name);
	printf("性别:%s\n", stu[i].sex);
	printf("年龄:%d\n", stu[i].age);
	printf("分数:%f", stu[i].score);
}

大致和普通变量没有区别,操作结构体的关键就是“.”。

1.3、结构体指针

结构体指针和其它变量的指针类似,而且用结构体指针操作结构体的用法也类似,唯一的不同就是在用结构体指针操作结构体成员。

(1)结构体指针的定义

结构体指针的定义和普通指针变量的定义是相似的,在未使用typedef时,结构体指针定义如下:

struct 结构体名称 *结构体指针变量名称;
//那么学生结构体指针定义如下
struct student *p;

而使用typedef之后,定义如下:

Student *p;

(2)结构体指针初始化结构体

在此之前先讲一下访问结构体成员的三种方式,假定有下列结构体和结构体指针:

//声明一个结构体变量和结构体指针变量
Student stu, *p;

那么访问成员的方式如下:

//通过“.”来访问结构体成员,结构体变量访问成员时,需要用“.”来访问
stu.name

//通过“->”来访问成员,结构体指针访问成员时,需要用“->”来访问
p->name

//先获取结构体指针指向的内容,然后再访问成员
*p->name

了解上面的东西后,就可以知道结构体指针初始化结构体了。

Student *p;
p->name = "zack";
p->sex = "male";
p->year = 1999;
p->score = 89;

然后我们做个简单的练习,创建5个学生结构体,然后根据学生的年龄排名。为了方便,我们重新创建一个结构体:

typedef struct{
	char name[20];
	int age;
}student; 

这个结构体就两个成员,我们可以使用冒泡排序:

//定义学生结构体数组,和临时变量temp
student stu[5], temp;
int i, j;
//循环输入5个学生的信息
for(i = 0; i < 5; i++){
	printf("请输入学生姓名:");
	scanf("%s", stu[i].name);
	printf("请输入学生年龄:");
	scanf("%d", &stu[i],age);
}
//使用排序算法排序
for(i = 0; i < 5; i++){
	for(j = i; i < 5; j++){
		if(stu[i].age > stu[j].age){
			temp = stu[i]
			stu[i] = stu[j];
			stu[j] = temp;
		}
	}
}

可以看到,结构体在按成员排序时和普通变量排序是一致的。

二、链表

之前说过指针在结构体中用处巨大,而这个用处实际体现在链表的实现。在具体讲解链表前,先了解一下线性表的概念。

2.1、线性表

因为不是讲解数据结构,所以对于线性表也是简单介绍。

(1)线性表

其实我们已经遇到过各种各样的线性表,最典型的就是数组了。虽然数组不等同于线性表,但是用数组理解线性表是个很好的选择。那么数组有什么特点呢?

  • 数据集合
  • 有序
  • 每两个相邻元素之间都有对应关系
  • 有开头有结尾

因为数组和线性表还是有区别的,所以并不代表上面的就是线性表的特点。不过有一点非常重要,就是“每两个相邻元素之间都有对应关系”,这个对应关系简单来说就是“一个前驱一个后继”。这么说有点不理解了,通俗讲就是“前面有一个,后面有一个”。这里的一个是指最多一个,也可以是零个。就好比珍珠项链,你任意以一个珍珠作为参考,任何一个珍珠都是前面一个,后面一个。

(2)顺序表和链表

上面简单说了一下线性表,然后我们来讲讲线性表和链表的关系。先看下面两张图:在这里插入图片描述
在这里插入图片描述
第一个是铁链,第二个并不是麻花啊,它是一条绳子。我说这两个都符合线性表的特点大家可能不是很理解,对于铁链还可以接受,但是绳子就有点牵强了。所以我们假设,绳子上面串了珍珠。

在假设绳子串珍珠之后,会发现铁链和绳子都符合“前一个后一个”的标准。我们也不深入研究了,现在我告诉大家这两个都是线性表。但是它们有什么区别呢?我们先看看铁链的特点:

  • 可以任意加长
  • 在中间添加链子非常方便

然后我们看看绳子:

  • 不能加长
  • 在中间添加珍珠要移动后面所有珍珠

这么一看链子要厉害许多,但是这只是对于链子和绳子来说。实际上链表和顺序表并不完全和上面一样。举这个例子只是作为直观的理解,链表具体的特点,我将在链表的实现当中为大家讲解。

2.2、链表介绍

(1)链表的实习原理

链表实际上是由一个个节点组成的,每个节点都存储两个东西。一个数据、还有一个地址。数据的话就是我们要用到的东西,地址存储的是下一个节点的地址。这样我们就能通过某一个节点找到下一个节点,经此而已。在这里插入图片描述
(2)用结构体模拟节点

结构体是一种构造数据类型,可以有许多类型不同的成员。然后我们根据节点的特点就可以用结构体模拟出一个节点了。
开始说节点存储两个数据,其实不准确。应该是两种数据,一个是我们要用到的数据,还有一个是下一个节点的指针。存放数据的区域叫做数据域,存放地址的区域叫做指针域。我们先来定义一个最简单的节点:

typedef struct num{
	//数据域
	int data;
	//指针域
	struct num *next;
}Node, LinkedList;

首先数据域没有什么疑问,就是一个普通的int数据。然后是指针域,因为我们是要存储下一个节点的地址,而节点又是一个num结构体,然后我们就可以根据上面学的结构体指针定义的方式定义一个结构体指针。

注意:在结构体中,内存大小必须是确定的。结构体的成员不允许是本身结构体变量,但是可以为本身的结构体指针。以为指针的大小是确定的,而如果成员中有本身的结构体变量系统将无法知道这个结构体应该分配多少内存。

结构体的具体实现将在下一篇为大家分享。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐