C语言指针五——指针应用:链表

——从代码猜测作者的意图毕竟是一件痛苦的事,这也是为什么许多人接手别人的项目宁愿从头做起也不愿意使用现有代码的原因。

链表是一种重要的数据结构,它是许多其他数组结构的和算法的基础,其重要性不言而喻。对于新手来说,它的确是学习C语言的一道关卡——希望你不要被它所吓倒,从而失去对编程的兴趣。

要先学习链表,必先要学习C语言的结构,有了它,我们可以将学生也定义也一个C语言的数据类型。看如下的代码:

#include

struct student

{

char *name;//学生的姓名

int age;//学生的年龄

char *id;//学生的学号

};

void PrintStudent(studentstu)

{

printf_s("学生的姓名为:%s,学生的年龄为:%d,学生的学号为:%s\n", stu.name, stu.age,stu.id);

}

int main()

{

student stu;

stu.name = "汪星人";

stu.age = 18;

stu.id = "12110031";

PrintStudent(stu);

return 0;

}

上面的代码定义了一个结构student,并且定义了一个函数打印student变量。可以看到student结构中包含了学生的姓名-年龄-学号数组,此后,结构student就可以当作一个C语言的数组类型来使用了。在main函数里,声明了一个student类型的变量stu,并且对stu里面的成员进行了赋值,最后打印了stu变量里的内容。

链表是如下所示的数组结构:

这里每一个大长方形代表一个结构,箭头表示指针,那么上面表达的意思就是在结构的数据成员里,有一个指针项,这个指针会指向一个结构,如此,多个结构就构成了一个链表。

定义一个拥有指针的数据结构,只需要稍微修改一下上面的结构:

struct student

{

char *name;//学生的姓名

int age;//学生的年龄

char *id;//学生的学号

struct student *next;//指向结构student类型的指针

};

上面的声明中,next是一个指向结构student的指针,它不能指向其他的结构或者其他的数据类型。

链表的每一个项称作结点,构造一个具有3个结点的链表:

每一结点的next项指向下一个链表的结点,第一个结点称作表头(head),这样我们就可以通过表头来遍历整个链表了,最后的结点的next项指向NULL表示那个是这个链表的尾(最后一项)。

手工创建上图的链表,代码如下:

#include

struct student

{

char *name;//学生的姓名

int age;//学生的年龄

char *id;//学生的学号

struct student* next; //指向结构student类型的指针

};

void PrintStudent(studentstu)

{

printf_s("学生的姓名为:%s,学生的年龄为:%d,学生的学号为:%s\n", stu.name, stu.age,stu.id);

}

void PrintLink(struct student*head)

{

while (head!=NULL)

{

PrintStudent(*head);

head = head->next;

}

}

int main()

{

student stu,stu2,stu3;

stu.name = "汪星人";

stu.age = 18;

stu.id = "12110031";

//

stu2.name = "喵星人";

stu2.age = 19;

stu2.id = "12110032";

//

stu3.name = "哈星人";

stu3.age = 20;

stu3.id = "12110033";

student *head=&stu;

stu.next = &stu2;

stu2.next = &stu3;

stu3.next = NULL;//将链表末尾的结点的指针成员指向NULL是非常的必要的,不然在使用链表时会发生错误。

PrintLink(head);

//PrintStudent(stu);

return 0;

}上面的代码声明了3个student结构类型的变量,并且都已经赋值,然后利用&取地址运算符取得stu的作为链表的表头。表头的下一个结点是stu2,stu2的下一个结点是stu3,最后将stu3的成员next赋值为NULL,标识整个链表的结束。然后利用上面的PrintLink函数输出了整个链表的内容。注意到PrintLink函数利用head!=NULL来判断链表的结束与否,所以,表尾的NULL是非常有必要的,否则将不知道链表何时结束。

程序输出如下:

利用VS工具,在return 0;处设置断点,得到如下的关系图:

可以看到,链表这个名字对于这种数据结构来说是非常形象的,它一环扣一环,有开始有终点。

上面的代码是通过声明普通变量,然后再通过使用取地址运算符&来构建一个链表的,但大多数情况下,C程序更喜欢通过动态分配内存的方式来构建链表。有些时候,第一种构建链表的方式是行不通的,这时就不行不使用动态分配内存的方式来构建链表了。

为了通过动态内存的方式来构建链表,就必须要认识如下几个函数:

void* malloc(size_t Size);

void* calloc(size_t nmemb,size_t Size);

这两个函数都是C语言的动态内存分配函数,其中malloc的形式参数Size是要分配的字节数,calloc形式的参数的意思是分配nmemb个Size大小的内存空间,这两个函数返回的都是所分配空间的首地址。所不同的是,calloc会对所全本的内存进行初始化,这样就确保了使用的安全。(在C++中,使用new关键字会更加方便,new 是C++动态内存分配的关键字),下面使用malloc函数来构建链表,修改的main函数如下:

int main()

{

student *stu,*stu2,*stu3;

stu = (student *)malloc(sizeof(struct student));//malloc函数返回void*,必须显式转换为student*;

stu2 = (student *)malloc(sizeof(struct student));

stu3 = (student *)malloc(sizeof(struct student));

stu->name = "汪星人";

stu->age = 18;

stu->id = "12110031";

//

stu2->name = "喵星人";

stu2->age = 19;

stu2->id = "12110032";

//

stu3->name = "哈星人";

stu3->age = 20;

stu3->id = "12110033";

student *head=stu;

head->next = stu2;

stu2->next = stu3;

stu3->next = NULL;//将链表末尾的结点的指针成员指向NULL是非常的必要的,不然在使用链表时会发生错误。

PrintLink(head);

return 0;

}

下面以stu变量为例,解释程序的行为:

首先是将stu声明为一个指向student的指针变量,它还没有初始化,还不能使用。然后使用malloc函数向操作系统申请了sizeof(struct student)大小的空间——它刚好是student变量所占的内存大小。然后将这个地址返回。因为它的返回值是void*,所以必须将其转换为student*才能赋值给stu。因为stu是一个存储了实际数据的地址,所以不能再用成员访问符“.”来访问name,age等student的成员了,毕竟stu没有成员。为了访问stu指向的student变量,可以将其转换成student后再使用,如(*stu).name,或者使用->符号,通过指针->成员的形式来访问指向变量的数据成员。图解如下:

因为name和id是指针型变量,所以当它们被赋值后,更详细的内存快照如下:

Also see:

上面的修改后的程序的输出的原始程序的输出是一样的。

上面创建链表的方式很容易理解,他相当直观。但这种创建链表的方式有很多多的局限性。首先,你可能不知道你要创建的链表有多少个结点;其次,如果要使用这种方式来创建一个具有100个结点的链表,代码太过于繁琐。因此我们需要有一种方式,它可以动态(根据需要)地创建链表。下面编写一个学生资料录入程序,是一个动态创建链表的示例。

程序要求:

1.根据需求动态地创建链表的每一个结点

2.它基于命令行输入学生的信息(为了简单起见,结构只有一个成员:学号)

3.输入0代表数据输入完成;

程序代码如下;

#include

#include

struct student

{

int id;//学生的学号

struct student* next; //指向结构student类型的指针

};

void PrintStudent(studentstu)

{

printf_s("学生的学号为:%d\n",stu.id);

}

void PrintLink(struct student*head)

{

while (head!=NULL&&head->id!=0)

{

PrintStudent(*head);

head = head->next;

}

}

student* DataEntry()

{//这个函数用于录入数据

student *head, *curr, *next;

head =curr= (student*)malloc(sizeof(struct student));//第一次分配的空间地址作为表头

while (scanf_s("%d",&curr->id))//如果数据读入成功

{

if (curr->id==0)//如果是结束输入的标志

{

curr->next = NULL;//将当前结点curr设置为表尾

break;

}

else

{

next = (student*)malloc(sizeof(struct student));//为下一个结点申请空间,并且返回内存空间的地址

curr->next = next;//链接下一个结点

curr = next;//为下一个结点赋值做准备

}

}

return head;

}

int main()

{

student *head;

head=DataEntry();

PrintLink(head);

return 0;

}

调试程序,在命令行输入如下:

DataEntry函数的运行过程如下图:

图有点乱,我也凌乱了。

运行结果输出如下:

下面再次详细说明运行过程:

首先是第一次申请结点的空间,这个结点的空间地址将作为表头。申请空间后,现在有了空间存放数据了,在屏幕输入id数,此时的内存快照如下:

然后新申请一个内存空间(结点),内存空间的首地址存入next中,并且将表头的next指向新申请的结点,此时内存快照如下:

然后curr指向新申请的结点,并且对新申请的结点的id项赋值,此时的内存快照如下:

再次新申请一个结点空间,返回空间的首地址给next,然后将curr结点的next项指向新申请的结点,此时内存快照如下:

然后curr指向新申请的结点,并且对新申请的结点的id项赋值,此时的内存快照如下:

再次申请一个结点空间,并且使next指向它,然后将curr结点的next项指向新申请的结点,新申请结点的id录入为0.于是新申请结点的next项值为NULL,构建链表结束,此时内存快照如下:

链表构建完成,本章到此结束!

c语言链表中何时用点何时用箭头,C语言指针五——指针应用:链表相关推荐

  1. c语言链表中何时用点何时用箭头,链表基本操作及其过程详细叙述

    链表概述:链表是一种常见的数据结构.数组可以存放数据,但是使用数组时要先指定数组中包含元素的个数,即数组长度.但是如果向这个数组中加入的元素个数超过了数组的大小时,便不能将内容完全保存.例如在定义一个 ...

  2. 如何将计算思维融合到C语言程序设计中,利用案例融合计算思维与C语言教学

    摘 要: C语言程序设计的教学目标是使学生利用C语言结合算法解决简单的实际问题.要实现该目标不仅要熟练掌握C语言本身的基本语法和结构,还要具备设计合理算法的思维能力.然而,当前各高校课程教学往往忽略思 ...

  3. c语言关键词中英翻译机编程,课程设计--C语言关键字中英翻译机

    <课程设计--C语言关键字中英翻译机>由会员分享,可在线阅读,更多相关<课程设计--C语言关键字中英翻译机(21页珍藏版)>请在人人文库网上搜索. 1.课课 程程 设设 计计 ...

  4. 以下所列的C语言字符常量中 合法的是,2013年计算机二级C语言课后模拟题二及答案...

    11.下面四个选项中,均是不合法的整型常量的选项是( D ). A) -0f1-0xffff 0011 B) -0xcdf01712456 C) -018999 5e2 D) -0x48eg-0680 ...

  5. c语言单链表中头结点的创立,一个关于C语言链表头结点的问题

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 #include #include #include typedef struct STU{ int sno; char sname[10]; float ...

  6. C语言链表交换相邻节点,LeetCode 24--两两交换链表中的节点 ( Swap Nodes in Pairs ) ( C语言版 )...

    题目描述  : 解题思路 : 分为两种情况处理 , 当要交换的节点是链表的前两个节点时 , 当交换的节点非前两个节点时 ; 代码如下 : /** * Definition for singly-lin ...

  7. c语言程序中的注释必须单独占一行,C语言习题第一章

    以下叙述中错误的是() A.一个C语言程序只能有一个主函数 B.C语言编写的每个函数都可以进行独立的编译并执行 C.C语言编写的函数都可以作为一个独立的源程序文件 D.C语言编写的函数源程序,其文件名 ...

  8. 在下面的c语言语句中存在错误的是,在下面的C语言语句中,存在错误的是int a=b=10;...

    C语言选择28下列定义变量的语句中错误的是A.floatUS$;B.doubleint_;C.charFor;D.int c语言中,能用来做标识符的只有字母,数字和下划线_并且标识符开头只能是字母或者 ...

  9. c语言程序中的函数的函数名,一个C语言程序是由一个或多个函数组成的,其中必须包含一个函数,函数名是mian。...

    解析: [解析题]绿茶的茶水比一般是1:50. [解析题]以下程序的输出结果是 ________ . struct HAR { int x;int y;struct HAR *p;}h[2];int ...

最新文章

  1. win2000.win2003关闭端口详解--防黑必备
  2. 分析模式:可复用的对象模型学习笔记
  3. 关于接口 RandomAccess
  4. OpenCV级联识别器cascade recognizer的实例(附完整代码)
  5. 视频加速方案的最优解 - Xilinx硬件加速技术专场
  6. 安装head插件依赖包grunt-cli
  7. Matlab对图像进行鼠标取点操作及K值聚类分析
  8. 算法高级(37)-微信、微博中的好友关系该如何设计?
  9. 很遗憾!iPhone 12内部CAD设计图流出:刘海并未缩小
  10. ceisum 加载geojson,webgl 加载geojson数据没有贴地
  11. 【Java从0到架构师】SpringMVC - 特殊的请求参数
  12. python运维书_python运维书
  13. 趋势OFFICESCAN忘记卸载密码时如何卸载客户端
  14. SNN系列|神经元模型篇(2) Izhikevich
  15. sis最新ip地址2020入口一_【新版教材】2020最新人教版高中历史教材必修一电子课本...
  16. 上古卷轴php代码,【上古卷轴五木柴代码】
  17. 爱快软路由设置DHCP多个LAN处于同一网段
  18. fiilt1左耳无法同步_FIIL T1 X真无线运动耳机体验:闪连快充秒同步 媲美AirPods
  19. 卸载python重新安装_Python卸载了怎么重装
  20. WiFi模块驱动移植

热门文章

  1. Java服务启动、重启、停止shell脚本模板
  2. 石家庄新华计算机学校在哪,石家庄新华电脑学校地址
  3. 基于php的汽车俱乐部会员管理系统——计算机毕业设计
  4. CANoe-发送CAN消息的几种方式
  5. C语言 基于文件的程序设计(PTA)
  6. 高亮版gbasp是什么屏幕_有光才更出色:GBA更换高亮屏
  7. JavaWeb项目总结
  8. vivoy73s和oppoa92s哪个好
  9. DASCTF7月 ez_forenisc
  10. kdevelop汉化