链表中二级指针作用(图示)_二级指针详细图解-程序员宅基地

技术标签: 二级指针  算法  链表  链表中二级指针作用(图示)  

前提知识

这个问题比较像函数传递时的值传递和引用传递类似,具体可以看下面两段代码,方便理解后面双指针。可以看到输出结果左侧还是6,而右侧输出0,这就是关键所在。形参m只是实参a的一个赋值的变量,形参我们都知道是函数调用时候才分配内存单元,当函数调用完毕后,形参就会被释放掉,所以左侧程序可以这么理解:定义一个a变量,它的值为6,当把a作为实参传进test这个函数时,系统会定义一个变量m,并且把a的值“6”赋给了m,然后又执行m=0。所以,到整个程序结束,m=0,a=6,所以a的值根本就没有发生改变。而右侧代码,a和m指向同一个地址,因此在test函数里m的修改对于main函数里的a同样有效。

	void test(int m){
                       void test(int *m){
    
        m = 0;                              *m = 0;
    }                                   }
    int main(){
                             int main(){
    
        int a = 6;                          int a = 6;
        test(a);                            test(&a);
        cout<<a<<endl;                      cout<<a<<endl;
        return 0;                           return 0;
    }                                   }

若一个变量想通过函数来改变本身的值,将本身作为参数传递是不成功的,只有传递本身的指针(地址)才能达到这样的效果。

所以后面我们创建链表时,传递的是**双指针**,这就是为什么参数是双指针的原因。

变量内存中都有自己的地址

我们来看下面一张图,就比较容易理解,只要是变量它都会有自己的地址(指针),即使是指针变量。

然后,指针它就是用来存地址的,只有两部分,一部分是附带自己的地址,一部分是存别人的地址
在这里插入图片描述

初始化链表需要双指针

定义链表节点如下代码所示:

struct ListNode {
    
    int val;
    ListNode *next;
    ListNode(int x,ListNode *t) : val(x), next(t) {
    }
};

现在又两段初始化链表头结点的代码,执行结果完全不一样:

	void test(){
                                          void test(){
    
        ListNode *head = NULL;                            ListNode *head = NULL;
        init(&head);                                      init(head);
        print(head);                                      print(head);
    }                                                 }
    void init(ListNode **node){
                           void init(ListNode *node){
    
        *node = new ListNode(10,NULL);                    node = new ListNode(10,NULL);
    }                                                 }
    void print(ListNode *head){
                           void print(ListNode *head){
    
        while(head != NULL){
                                  while(head != NULL){
    
            cout<<head->val<<"->";                            cout<<head->val<<"->";
            head = head->next;                                head = head->next;
        }                                                 }
        cout<<endl;                                       cout<<endl;
    }                                                 }

在这里插入图片描述
可以看出执行结果,左侧使用二级指针成功初始化插入节点,而右侧不行。如果传的是一级指针,那么仅仅只是head和node所指的是同一块内存区域,而node和head本身并不是同一个指针,函数执行完,node指针也被释放掉,而新生成的节点在内存中孤零零,没有人指向它。

使用双指针情况

下面来进行详细图解一下:
首先看使用双指针情况,对于在test函数中定义的head节点,定义了一个指针,指向ListNode 类型,其实也就是整个链表的头指针,只不过还没有赋值。

ListNode *head = NULL;

在这里插入图片描述
下面调用节点初始化函数,head本身就是指针,现在&head即为取地址,相当于将head指针的地址传递过去,刚好对应于初始化函数void init(ListNode **node)中的二级指针,其中**node表示指针的指针。内侧的指针*node指向链表,即对应于*head,用来初始化,外侧的**head指向链表指针,相当于上面讲的指针传递,这样才可以把init函数中node的修改作为参数传回给主程序。

init(&head);
其原型函数是 void init(ListNode **node)

图示如下:
在这里插入图片描述
其实*node就是头指针head的值了,加*号就代表指针的值,new会申请一个结点,然后返回结点的首地址,其实这个新生成的结点是没有名字的,为了方便假设为x。具体图示如下图所示

*node = new ListNode(10,NULL);

在这里插入图片描述
为了更清晰说明我们写代码打印每一个地址,代码如下以及执行结果

	void test(){
    
        ListNode *head = NULL;
        cout<<"1."<<&head<<endl;
        init(&head);
        cout<<"7."<<head<<endl;
        print(head);
    }
    void init(ListNode **node){
    
        cout<<"2."<<&(*node)<<endl;
        cout<<"3."<<*node<<endl;
        cout<<"4."<<(node)<<endl;
        *node = new ListNode(10,NULL);
        cout<<"5."<<(*node)<<endl;
        cout<<"6."<<node<<endl;
    }

在这里插入图片描述
下面使用图示对每步操作,地址变化做详细说明:
在这里插入图片描述
在这里插入图片描述

使用单指针情况

如果理解上面的情况,再分析右侧代码不使用双指针,就比较好分析了。编译器总是要为函数的每个参数制作临时副本,比如看输出的1和2,看到head和node的地址都不一样,在函数init中node指向生成的头指针,然而函数结束后,head还是原来那样没什么变化,node被销毁释放掉,至于新生成的结点,则是孤零零的在内存区里瑟瑟发抖,等待有人来指向它,这就是需要双指针原因。用了双指针,head指向了新生成的结点,node被释放掉,皆大欢喜。

	void test(){
    
        ListNode *head = NULL;
        cout<<"1."<<&head<<endl;
        init(head);
        cout<<"7."<<head<<endl;
        print(head);
    }
    void init(ListNode *node){
    
        cout<<"2."<<&node<<endl;
        cout<<"3."<<node<<endl;
        node = new ListNode(10,NULL);
        cout<<"4."<<node<<endl;
        cout<<"5."<<&node<<endl;
        cout<<"6."<<node->val<<endl;
    }

在这里插入图片描述
具体图示如下:
在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yuanfuli/article/details/102880988

智能推荐

javaweb系统oracle锁表,java+oracle+web(第八天) java 基础课程(一) 线程同步和死锁...-程序员宅基地

文章浏览阅读65次。synchronizedsynchronized同步代码块同步函数两种方式。/*死锁,共享资源自有自己的锁,然后相互访问都要解锁,如果相互拿不到对方的锁,可能产生死锁synchronized例子说明:两个锁中锁,A和B,然后A有资源,然后B锁在A中锁了一个资源。然后两个线程来访问。引起死锁。在开发中要避免死锁。*/classLockTest implements Runnable{private ..._oracle两个线程同时锁一张表

中英文输入_鼠标复制英文加数字 site:blog.csdn.net-程序员宅基地

文章浏览阅读355次。一、键盘分为那几个部分: 1.功能区 2.指示灯区 3.打字键盘区 4.编辑区 5.数字键盘区 二、规范打字: 1.食指:(左边)45 rt fg vb (右边)67 yu hj nm 2.中指:(左边)3edc (右边)8ik, 3.无名指:(左边)2w..._鼠标复制英文加数字 site:blog.csdn.net

Pico四通道汽车诊断示波器高级套装(型号:PQ180)-程序员宅基地

文章浏览阅读993次。Pico汽车诊断示波器把你的PC电脑变成一台功能强大的汽车诊断工具,快速查找现代车辆上日益增加的传感器,执行器和电路的故障。12位高分辨率的PC示波器模块连接到你的电脑的USB接口上,并且能够采集达250M样本,使它能够捕捉复杂的汽车波形,包括CAN总线和FlexRay信号;然后放大可疑区域。由于基于PC,这些波形可以存储到你的PC电脑内用于以后参考,打印或发e-mail。强大的、容易使用的PicoScope汽车软件。整套套装里的一个重要组件就是功能强大的PicoScope汽车诊断软件。使用Pico_pq180

通过Java反射获得对象里面的所有字段名以及字段对应的值_java 通过对象 反射获取 对象的所有字段值-程序员宅基地

文章浏览阅读2.2w次,点赞4次,收藏9次。首先我们有一个对象类package com.xuzihui.ref;public class Student { public Student() { } private int id; private String name; private int math; private int english; public int getId() { retur..._java 通过对象 反射获取 对象的所有字段值

微信小程序开发笔记⑪——数据缓存、数据传输方式、罗盘、wifi、性能监控、加速计和剪切板_微信小程序监控缓存内容-程序员宅基地

文章浏览阅读334次。数据缓存官方描述https://developers.weixin.qq.com/miniprogram/dev/api/storage/wx.setStorageSync.html每个微信小程序都可以有自己的本地缓存,可以通过wx.setStorage/wx.setStorageSync、wx.getStorage/wx.getStorageSync、wx.clearStorage/w..._微信小程序监控缓存内容

#HDU By Recognizing These Guys, We Find Social Networks Useful (割边 + 链式前向星)_hduby-程序员宅基地

文章浏览阅读345次。By Recognizing These Guys, We Find Social Networks UsefulTime Limit: 2000/1000 MS (Java/Others)Memory Limit: 125536/65536 K (Java/Others)Total Submission(s): 4200Accepted Submission(s): 108..._hduby

随便推点

异构计算技术分析_异构计算对神经网络的影响-程序员宅基地

文章浏览阅读1.2k次,点赞3次,收藏8次。异构计算技术分析_异构计算对神经网络的影响

vsport 虚拟串口 控件 Virtual Serial Port ActiveX注册码_virtual serial port driver注册码-程序员宅基地

文章浏览阅读2.4w次,点赞2次,收藏4次。使用VSport.dll 开发了虚拟串口工具 在开发电脑上 安装好驱动后 可以运行 正常创建虚拟串口 Virtual Serial Port ActiveX提供 vsport注册码,该注册码可以正常注册使用,注册码没有任何限制,但仅供测试使用,谢谢virtual Serial Port ActiveX Control Installation Here is a step-by-step inst..._virtual serial port driver注册码

Qt之Windows使用openssl(RSA加密)_qt openssl windows-程序员宅基地

文章浏览阅读1.2k次,点赞2次,收藏14次。抄袭:大佬openssl下载和qt例程:链接:https://pan.baidu.com/s/15avdzMeHgmB1qiWXB63Qow提取码:ulja代码:.pro文件添加了外部库#-------------------------------------------------## Project created by QtCreator 2022-04-14T10:20:16##---------------------------------------------._qt openssl windows

springboot 解析前端传来的时间字符串后端LocalDateTime解析失败 could not be parsed, unparsed text found at index 10-程序员宅基地

文章浏览阅读5.5k次。springboot 解析前端传来的时间字符串后端LocalDateTime解析失败 could not be parsed, unparsed text found at index 10_could not be parsed, unparsed text found at index 10

Chrome教程之NetWork面板分析网络请求-程序员宅基地

文章浏览阅读511次。官方文档:https://developers.google.com/web/tools/chrome-devtools/network/最近打算写一写Chrome教程文档,不知道大家最感兴趣的是什么内容呢?1.如何打开无论是在Windows还是Mac,都可以使用(FN)+F12键打开Chrome的Network面板。2.面板组成如图所示,Chrome的Network面板主要由5个部..._chrom network选择多个类型

HTTPS面试常问全解析,在线面试指南-程序员宅基地

文章浏览阅读156次。kafka面试基础[17]1.Kafka的用途有哪些?使用场景如何?2.Kafka中的ISR、AR又代表什么?ISR的伸缩又指什么3.Kafka中的HW、LEO、LSO、LW等分别代表什么?4.Kafka中是怎么体现消息顺序性的?5.Kafka中的分区器、序列化器、拦截器是否了解?它们之间的处理顺序是什么?6.Kafka生产者客户端的整体结构是什么样子的?7.Kafka生产者客户端中使用了几个线程来处理?分别是什么?8.Kafka的旧版Scala的消费者客户端的设计有什么缺陷?9.“消费组

推荐文章

热门文章

相关标签