jjzjj

C语言关于链表的代码看不懂?一篇文章让你拿捏二级指针并深入理解函数参数列表中传参的多种形式

微凉秋意 2024-05-24 原文

✅作者简介:别人以梦为马,而我要以码为梦。我是叶落秋白,努力学后端中

✨个人主页:叶落秋白的主页

🔥系列专栏:数据结构干货分享

📃推荐一款模拟面试、刷题神器👉进入刷题的世界

🔥前言

        这篇博客即将解决你看不懂或者不会写链表的基本操作的问题,对于初学者而言,有很多地方肯定是费解的。比如函数的参数列表的多样化,动态分配内存空间函数malloc等,其实这些知识和指针联系紧密,尤其是二级指针。那么开始好好的学习这篇博客吧!

📃目录

二级指针讲解

链表的应用 

定义双链表的结构体

创建双链表

传入一级指针

传入指针的引用

传入二级指针

✨刷题网推荐


二级指针讲解

简述:其实就是一个指针指向另一个指针的地址

我们都知道指针指向地址,但是指针自身也是一个变量,当然也可以被二级指针所指向。

语法:形如 int x = 10; int *q = &x; int **p = & q;

那么这里的q指针指向x的地址,p指针指向指针q的地址,*q可以得到x的值,*p可以得到q指针本身,**p也可以得到x的值。

代码示例:

int main(void)
{
	int x = 10; 
	int* q = &x;
	int** p = &q;	
	printf("x 的地址为:    %d\n", &x);
	printf("q 指向的地址为:%d\n", q);
	printf("*p的值为:      %d\n", *p);   //p指向指针q的地址,那么*p是解引用操作,
                                          //就等于了q本身
	printf("x 的值为:     %d\n", x);
	printf("q 存取的值为: %d\n", *q);
	printf("**p的值为:    %d\n", **p);    //**p相当于解引用解了两次,第一次先得到q本身,
                                          //第二次得到q指向地址的值
	return 0;
}

运行结果:

链表的应用 

        这里以带头结点的双链表为例

定义双链表的结构体

typedef int ElemType;//将整型数据重命名为int
typedef int Status;//整型重命名为Status

//双链表的数据结构定义
typedef struct DouNode {
    ElemType data;               //数据域
    struct DouNode* head;        //前驱指针
    struct DouNode* next;        //后继指针
}DousList, * LinkList;// 结点指针

代码解释:

        利用typedef对数据类型进行重命名,只要在后面遇到的 ElemTypeStatus都是整型就够了。双链表结构体包含三个部分:数据域、前驱指针、后继指针,与单链表的区别就是多了一个前驱指针。然后大括号结束部分也是重命名,此时DousListDouNode效果一样,都是结构体名,然后LinkList是指向结点的指针。

具体使用:

        LinkList L,L是一个指针,DousList *P,P也是一个指针,属于两种创建方式。

创建双链表

        使用两种正确的创建链表形式和一种错误的形式,对比着记忆创建方法

传入一级指针

        这种方式并不能成功创建

代码演示:

void CreateDouList(LinkList L, int n)
{
    LinkList  ptr;
    int i;
    L = (LinkList)malloc(sizeof(DousList));    //为头结点申请空间
    L->next = NULL;
    L->head = NULL;
    L->data = n;//L->data记录结点的个数
    ptr = L;
    for (i = 0; i < n; i++)
    {
        int value = 0;
        scanf("%d",&value);
        LinkList me = (LinkList)malloc(sizeof(DouNode));
        me->data = value;    //节点数据域
        me->next = NULL;
        me->head = NULL;
        ptr->next = me;     
        me->head = ptr;
        ptr = ptr->next;     //尾插法建表
    }
}

代码解析: 

         这里的参数列表是 LinkList L 和 整型数据 n,L是传入的链表头结点指针,n是用来记录插入数据的个数的,在下面的for循环用做循环的次数。接下来使用malloc函数为L链表分配内存空间,malloc需要用指针来接收,左边的括号是分配的指针类型,右边的括号是分配的内存空间大小。分配空间完成之后初始化前驱和后继指针为空,数据域data记录数据的个数。ptr指针初始等于L指针,接下来进入n次循环,创建待插入结点指针me并进行分配内存空间和初始化,最后三行代码进行尾插法建立链表:

尾插法:

        先让ptr的后继指针指向me,然后me的head指针指向ptr,这就相当于在链表头把me结点插入链表,然后ptr指向这个插入的新结点,这就保证了每次插入的结点都在上一个插入的结点之后。

但是这样真的在链表中插入数据了吗 ,来看看调试结果:

        进入遍历的程序时,让创建的ptr指针指向L链表的后继,立马就出现了空指针异常,但是上面明明插入数据了,原因是什么呢? 很明显,这里的链表L并未完成插入数据。这是因为我们在创建链表的函数里传入的只是链表的指针L,那么在函数里这个指针只是一个副本,在这里给他增大内存空间并不会影响到实参链表,这和普通数据类型的值传递和地址传递的情况一致。

        我们利用传入指针地址来解决这个问题,两个方法:指针的引用二级指针

传入指针的引用

        函数的实现部分完全不用修改,只要形参列表加上一个引用符"&"即可。

void CreateDouList(LinkList &L, int n);

 查看调试结果:

        同样的调试方法,传入指针的引用之后可以清晰的看到L的data等于5,也就是存了五个数据,然后对于的后继结点的值都和尾插的结果一致,最后一个结点的后继指针正好指向NULL,完全符合我们的设计的代码。

        传入指针的引用之后,函数里链表的空间变化会导致实参里的链表空间变化,这样做才能使插入操作完成,将结点插入到链表内。

传入二级指针

        这个和指针的引用原理一样,我主要分享给你们使用的形式

    注意调用的时候实参要加“&”符,例:CreateDouList(&L,n);

void CreateDouList(LinkList *L, int n)
{
    LinkList  ptr;
    int i;
     *L = (LinkList)malloc(sizeof(DousList));    //为头结点申请空间
    (*L)->next = NULL;
    (*L)->head = NULL;
    (*L)->data = n;//L->data记录结点的个数
    ptr = (*L);
    printf("开始插入数据:\n");
    for (i = 0; i < n; i++)
    {
        int value = 0;
        scanf("%d",&value);
        LinkList me = (LinkList)malloc(sizeof(DouNode));
        me->data = value;    //节点数据域
        me->next = NULL;
        me->head = NULL;
        ptr->next = me;     
        me->head = ptr;
        ptr = ptr->next;     //尾插法建表
    }
}

        这里形参列表的参数是 LinkList *L,和DousLIst **L效果一样,是一个二级指针。如果用到指向链表的指针就需要一次接引用操作,写成(*L)的形式。然后再去分配空间、进行初始化、赋值给链表指针ptr等操作,这样链表二级指针L的改变也会使实参的链表发生改变,可以查看调试结果。

调试结果:

✨刷题网推荐

        数据结构对于小白来说并不是特别友好,好多知识几天不看就会抛之脑后一定要多巩固学到的知识,因此刷题必不可少。在这里我给大家介绍一个我认为对小白比较友好的刷题平台—牛客网

这些都是经典的链表题,多加练习一定可以巩固知识,拿捏个链表还会是问题吗。

        希望大家可以充分利用时间,巩固所学,冲击大厂,让我们共同进步!

    

有关C语言关于链表的代码看不懂?一篇文章让你拿捏二级指针并深入理解函数参数列表中传参的多种形式的更多相关文章

  1. ruby - nanoc 和多种布局 - 2

    是否可以为特定(或所有)项目使用多个布局?例如,我有几个项目,我想对其应用两种不同的布局。一个是绿色的,一个是蓝色的(但是)。我想将它们编译到我的输出目录中的两个不同文件夹中(例如v1和v2)。我一直在玩弄规则和编译block,但我不知道这是怎么回事。因为,每个项目在编译过程中只编译一次,我不能告诉nanoc第一次用layout1编译,第二次用layout2编译。我试过这样的东西,但它导致输出文件损坏。compile'*'doifitem.binary?#don’tfilterbinaryitemselsefilter:erblayout'layout1'layout'layout2'

  2. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  3. TimeSformer:抛弃CNN的Transformer视频理解框架 - 2

    Transformers开始在视频识别领域的“猪突猛进”,各种改进和魔改层出不穷。由此作者将开启VideoTransformer系列的讲解,本篇主要介绍了FBAI团队的TimeSformer,这也是第一篇使用纯Transformer结构在视频识别上的文章。如果觉得有用,就请点赞、收藏、关注!paper:https://arxiv.org/abs/2102.05095code(offical):https://github.com/facebookresearch/TimeSformeraccept:ICML2021author:FacebookAI一、前言Transformers(VIT)在图

  4. ruby - 易于初学者理解的 Ruby 库 - 2

    关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。关闭3年前。Improvethisquestion我正处于学习Ruby的阶段,我想查看一些小型库的源代码以了解它们是如何构建的。我不知道什么是小型图书馆,但希望SO能推荐一些易于理解的图书馆来学习。因此,如果有人知道一两个非常小的库,这是新手Rubyists学习的好例子,请推荐!我想使用Manveru'sInnatelib,因为它试图保持在2000LOC以下,但我还不熟悉其中经常使用的Ruby速记。也许大约100-5

  5. ruby - 无法理解 `puts{}.class` 和 `puts({}.class)` 之间的区别 - 2

    由于匿名block和散列block看起来大致相同。我正在玩它。我做了一些严肃的观察,如下所示:{}.class#=>Hash好的,这很酷。空block被视为Hash。print{}.class#=>NilClassputs{}.class#=>NilClass为什么上面的代码和NilClass一样,下面的代码又显示了Hash?puts({}.class)#Hash#=>nilprint({}.class)#Hash=>nil谁能帮我理解上面发生了什么?我完全不同意@Lindydancer的观点你如何解释下面几行:print{}.class#NilClassprint[].class#A

  6. ruby - 如何理解 Ruby 中的发送者和接收者? - 2

    我很难理解Ruby中sender和receiver的实际含义。它们一般是什么意思?到目前为止,我只是将它们理解为方法调用和获取其返回值的调用。但是,我知道我的理解还远远不够。谁能给我一个Ruby中发送者和接收者的具体解释? 最佳答案 面向对象中的一个核心概念是消息传递和早期概念化,这在很大程度上借鉴了计算的Actor模型。艾伦·凯(AlanKay)创造了面向对象一词并发明了最早的OO语言之一SmallTalk,他拥有voicedregretatusingatermwhichputthefocusonobjectsinsteadofo

  7. 多种方法期间的 Ruby 救援异常 - 2

    我构建了一个简单的银行应用程序,它能够执行通常的操作;充值、提现等我的Controller方法执行这些操作并拯救由帐户或其他实体引发的异常。以下是Controller代码中使用的一些方法:defopen(type,with:)account=createtype,(holders.findwith)addaccountinit_yearly_interest_foraccountboundary.renderAccountSuccessMessage.new(account)rescueItemExistError=>messageboundary.rendermessageendde

  8. ruby-on-rails - Rails - 理解 application.js 和 application.css - 2

    rails新手。只是想了解\assests目录中的这两个文件。例如,application.js文件有如下行://=requirejquery//=requirejquery_ujs//=require_tree.我理解require_tree。只是将所有JS文件添加到当前目录中。根据上下文,我可以看出requirejquery添加了jQuery库。但是它从哪里得到这些jQuery库呢?我没有在我的Assets文件夹中看到任何jquery.js文件——或者直接在我的整个应用程序中没有看到任何jquery.js文件?同样,我正在按照一些说明安装TwitterBootstrap(http:

  9. ruby-on-rails - rails delete_if 使用哈希忽略当前文章(中间人) - 2

    我为你们准备了一个简单的。我想要一个特色内容部分,其中排除了当前文章所以这可以通过delete_if使用MiddlemanBlog:但是我使用的是中间人代理,所以我无法访问current_article方法...我有一个YAML结构,其中包含以下模拟数据(以及其他数据),文件夹设置如下:data>site>caseStudy>RANDOM-ID423536.yaml(由CMS生成)在每个yaml文件中,您会发现如下内容::id:2k1YccJrQsKE2siSO6o6ac:title:Heyplace我的config.rb看起来像这样data.site.caseStudy.eachdo

  10. ruby - 在 YARD 中使用多种方法记录鸭子类型 - 2

    YARD允许我指定方法参数和返回值的类型。因为我真的很喜欢躲避类型,所以很高兴看到YARD还支持通过指定它们必须支持的方法来定义类型。如你所见here,像#first_method,#second_method这样的表达被解释为逻辑析取。这意味着对象需要支持#first_method或#second_method或两者。这不是我需要的。我希望能够指定一个对象需要同时支持我的参数的#first_method和#second_method。有办法指定吗? 最佳答案 没有用于指定复合鸭子类型接口(interface)的惯用语法。也就是说,

随机推荐