在学习线性表的存储结构中,很多人在学习线性表的链式存储结构即单链表时,有人会注意函数传参LinkList L与LinkList *L的问题,如下
下面展示一些 内联代码片
。
#include<stdio.h>
#include<stdlib.h>
typedef int Status;
typedef struct Node
{
ElemType data; //数据域
struct Node* Next; //指针域(指向节点的指针)
}Node;
typedef struct Node* LinkList;;
Status GetElem(LinkList L, int i, ElemType* e) //获取单链表L中的第i个元素,用e返回其值。
Status ListInsert(LinkList *L, int i, ElemType e)//向单链表第i个位置插入新元素e。
有人会发现为什么获取单链表的元素形参输入是LinkList L,而向单链表插入或者删除等操作要用到LinkList *L。
我从以下几个方面逐一递进解释:
1、对于LinkList L: L是指向定义的Node结构体的指针,因为我们前面用typedef struct Node* LinkList,就是LinkList相当于struct Node*,这里的*是跟Node的后面,LinkList是一个指向该结构体的的指针的别名,故此可以用->运算符来访问结构体成员,即L->data,而(*L)就是个Node型的结构体了,可以用点运算符访问该结构体成员,即(*L).elem;
而对于LinkList *L:L是指向定义的Node结构体指针的指针,也就是说L的内容是指向定义的Node结构体指针的地址,(*L)是指向Node结构体的指针,注意,这里的(*L)要理解好。
2、从上面的定义我们知道LinkList L的L是一级指针,而后面的LinkList *L是二级指针,对于一级指针我们都知道,可以通过改变的指针的值从而改变量,如
#include<stdio.h>
int main()
{
int a = 100;
int b = 200;
int* p;
p = &a;
printf("%d,",*p);
p = &b;
printf("%d\n",*p);
return 0;
}
执行结果打印出来是100,200,指针p第一次取的是整型变量a的地址,此时*p的值为100,第二次取的是整型变量b的地址,p的值为100,可以看出通过改变指针p指向的地址,对应p的值也就不同,这就是一级指针的用法。
而一级指针可以运用在线性表的顺序存储结构或者数组中,因为其逻辑上具有线性关系的数据按照前后的次序全部存储在一整块连续的内存空间中,之间不存在空隙。注意这段话:使用顺序存储结构存储的数据,第一个元素所在的地址就是这块存储空间的首地址。通过首地址,可以轻松访问到存储的所有的数据,只要首地址不丢(即我们能找到首地址)数据永远都能找着,这就是为什么我们可以使用一级指针而不用二级指针,以下代码1,2说明:
代码1
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define MAXSIZE 20
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
typedef int ElemType;
typedef struct
{
ElemType data[MAXSIZE];
int length;
}SqList;
/******************向线性表第i个位置插入新元素e****************/
Status ListInsert(SqList* L, int i, ElemType e)
{
int k;
if (L->length == MAXSIZE)
{
return ERROR;
}
if (i<1 || i> L->length + 1)
{
return ERROR;
}
if (i <= L->length)
{
for (k = L->length - 1; k >= i - 1; k--)
{
L->data[k + 1] = L->data[k];
}
}
L->data[i - 1] = e;
L->length++;
return OK;
}
上面程序我们定义一个结构体指针*L,通过->运算符来访问结构体成员,而我们向线性表第i个位置插入的时候,后面的元素对应的地址会向后移,但是我们知道线性表的顺序存储结构或者数组中,其逻辑上具有线性关系的数据按照前后的次序全部存储在一整块连续的内存空间中,之间不存在空隙,故我们不需要指针内容发生改变,可以直接==L->data[k + 1] = L->data[k]==直接往后移。如果不理解可以看下图:
代码2
下面展示一些 内联代码片
。
#include<stdio.h>
#include<stdlib.h>
#define ERROR 0
#define OK 1
typedef int ElemType;
typedef int Status;
typedef struct Node
{
ElemType data; //数据域
struct Node* Next; //指针域(指向节点的指针)
}Node;
typedef struct Node* LinkList;
Status GetElem(LinkList L, int i, ElemType* e)
{
int j;
LinkList p;
j = 1;
p = L->Next;
while (p && j < i)
{
p = p->Next;
j++;
}
if( !p|| j>i)
{
return ERROR;
}
*e = p->data;
return OK;
}
代码2是单链表的取值操作,对于单链表,其链式存储结构中,除了要存储数据元素信息外,还要存储它的后继元素的存储地址(指针)。这就是表明它的内存地址不连续。但是对于取值操作,我们只需定义一个指向定义的Node结构体的指针L。因为是取值而没对链表的内容进行操作,所以我们可以通过一级指针指向结构体,结构体内部存有指向下个结点的指针域,进而帮助我们找到其值。到了这里我们就说明了LinkList L的意义了。
3、对于 LinkList *L,L是二级指针,前面我们说L的内容是指向定义的Node结构体指针的地址,故此(*L)是指向Node结构体的指针的地址。例如寻找或者修改数组的内容我们可以通过修改一级指针的值,因为内存地址是连续的,那么我们只要找到首地址就能把这个数组表达出来。而对于单链表内存地址不连续的,我们在对单链表进行增删等操作的时候,指针的内容会发生变化的,故此我们需要二级指针来改变一级指针的内容,如下代码3:
代码3
Status ListInsert(LinkList *L, int i, ElemType e)
{
int j;
LinkList p, s;
p = *L;
j = 1;
while (p && j < i)
{
p = p->Next;
j++;
}
if (!p || j > i)
{
return ERROR;
}
s = (LinkList)malloc(sizeof(Node));
s->data = e;
s->Next = p->Next;
p->Next = s;
return OK;
}
代码3定义一个二级指针LinkList *L,其中我们发现单链表插入的两条主要程序:s->Next = p->Next; p->Next = s;其中新结点s->Next是原来的p->Next,而原来的p->Next是新的结点s,此时原来p的内容已经发生了改变,即指向第i个位置的指针内容发生了变化,故此我们使用二级指针来改变p,第二次注意(*L)是指向Node结构体的指针,通过使用二级指针单链表在函数调用后就会有一个全新的内容,总的来说有点像递归,但是使用二级指针更是方便。在如下代码4也可以看出:
代码4
void InitList(LinkList *L)
{
*L = (LinkList)malloc(sizeof(Node));
(*L)->next = NULL; //由于->的优先级高于*,故此得加括号(*L)
}
代码4可看出,初始化空链表,函数调用完毕后,L会指向一个空的链表,即会改变指针的内容,故要用*L到了。到此我们就说明了LinkList *L的意义了。
*总结一句话:如果能理解的话,就记得但凡要修改L的值的操作都要使用 L,如果不修改L的值,用Linklist L 。
文章浏览阅读9.1k次,点赞6次,收藏9次。hive 中有case when 的语法是:case when 条件1 then 结果1when 条件2 then 结果2when 条件3 then 结果3......else 结果x end那如果被查询的行同时符合条件1和条件3呢?结果会是出现“结果1”还是“结果3”呢?根据测试,是符合结果1,原因是语句先“碰见” when 条件1 then 结果1这一句。如果语句改为:se..._hive case when then
文章浏览阅读232次。今天内容1.信息收集(收集目标所有可以收集的信息) 工具 客户端 网页端域名信息(子域名)站点信息端口信息敏感信息2.扫描探测(awvs xray)漏洞的入口点——————————————————PPT:域名解析过程:用户–>浏览器输入baidu.com -->浏览器DXS服务器缓存–>系统缓存dns服务器缓存C://windows/system32/drivers/etc/host–>dns服务器(发送请求)whois查询备案域名划分子域名_中北网络域名
文章浏览阅读707次。一、学习内容本次课学习了函数的基本知识,需要大家对如下知识点进行总结:1. 函数定义的基本格式,函数定义和函数原型(声明)的区别何在?2. 函数的调用方式有哪几种3. 什么是形参,什么是实参,函数调用时的参数传递机制是什么?二、实验内容1.定义一个判断素数的函数isprime(int n),利用该函数输出1000以内的所有素数,每行10个,最后输出一共有多少个素数。(每列对齐)2.求两个正整数的最..._调用fun函数求最大公约数
文章浏览阅读3.8k次,点赞56次,收藏33次。在使用mybatis-plus开发需求的时候会发现对于大部分的业务场景来说都会使用到join来进行联表查询,但是mybatis-plus封装的 mapper 不支持 join,如果需要支持就需要自己手动去实现,给大家推荐一个好用的插件(Mybatis-Plus-Join(简称 MPJ)是一个 Mybatis-Plus的增强工具,在 MyBatis-Plus 的基础上只做增强不做改变,为简化开发、提高效率而生。_mybatisplus一对多
文章浏览阅读106次。基于JAVA学生信息管理系统计算机毕业设计源码+系统+mysql数据库+lw文档+部署。springboot基于springboot和vue的酒店管理系统。springboot基于SpringBoot的自助旅游导航系统。springboot基于JSP的企业办公管理系统设计与实现。JSP宠物食品店系统的设计与实现sqlserver。ssm基于Java的幼儿早教系统软件的设计与实现。ssm基于vue的健康餐饮管理系统的设计与实现。ssm基于JAVA的求职招聘网站的设计与实现。
文章浏览阅读113次。一. 基本步骤 1.1环境准备 开始前,请确认gcc g++开发类库是否装好,默认已经安装。 注:等待linux下载更新功能准备好了 重启系统 在执行下载安装命令,如执行命令没有问题可以继续往下走 1. 最小Ubuntu安装插件 1. 需要安装 sudo apt-get install build-essen..._snail mock
文章浏览阅读288次。前几天分享了一篇网页代码高亮插件highlight.js,效果还不错,因为highlight.js本身没有行号展示,对于观感和体验上来源还是有点不太友好哈,恩。这几天越看越难受,忍不了还是弄一下吧。操作完成,上面代码框展示的就是配置后的效果了,因为我不大喜欢侧那块灰色的条,所以在css里面注释了,因人而异吧,喜欢的可以自己打开试下效果。_highlightjs/vue-plugin 行号
文章浏览阅读1.6k次。本文介绍了XP系统上创建宽带连接的方法;如果用户办理的是PPPoE(ADSL虚拟拨号)宽带业务,在未使用无线路由器的情况下,需要通过电脑上的“宽带连接”来拨号上网;如果有路由器,则不需要通过宽带连接来拨号上网了。下面本文详细介绍XP系统上创建宽带连接的方法。XP创建宽带连接1、右击“网上邻居”选择“属性”XP系统上网上邻居属性2、选择“创建一个新的连接”,如下图所示选择创建一个新的连接3、直接点击..._xp系统新设置宽带连接
文章浏览阅读103次。Pro LINQ:Language Integrated Query in C# 2008MS Press - Introducing Microsoft LINQLINQ for Visual C# 2005 (07年6月出版)LINQ for VB 2005 (07年6月最新PDF文字版)Manning:LINQ in ActionPro C# 2008..._c++ c# vb.net
文章浏览阅读1.1k次。confluence企业wiki搭建部署_ata confluence
文章浏览阅读830次。今天在创建SpringCloud项目过程中遇到了一个坑:当我将SpringCloud项目架子搭好之后,启动Eureka的时候报错(具体的错误提示忘记截图了),然后对问题摸索了好久之后才发现是SpringBoot与SpringCloud对应的版本问题。由于我项目中SpringBoot项目的版本用的是2.2.X,而SpringCloud的版本用的是 Greenwich.SR2所以造成了报错导致Eur..._springboot 2.1.1 对于的cloud
文章浏览阅读467次。无论是工作中还是生活里都离不开电脑,因为电脑可以用来编辑成我们需要的文件,还可以存储_磁盘恢复 csdn