单双链表及其反转

一,空指针的补充

1. 空指针的定义

在 C 语言中,空指针通常被定义为 NULL,或者在 C++ 中为 nullptr。它的本质是一个指针,指向无效的地址,用来表示一个指针当前没有指向有效的内存空间。空指针并不指向实际的内存地址,因此可以用于表示指针没有被初始化或者没有指向任何有效的对象。

例如:

 

int *ptr = NULL; // ptr 是一个空指针

在许多编译器中,空指针通常会被定义为 0,或者一个特定的常量值(例如 0x0 或 0xFFFFFFFF 等)。

2. 空指针在内存中的表示

空指针通常被定义为某个特定的内存地址,最常见的就是 0x0(即地址为 0)。这意味着空指针指向的是内存地址 0。内存地址 0 通常被保留,不能被程序正常使用。当程序试图访问内存地址 0 时,CPU 会触发一个 段错误(segmentation fault)或 访问违规(access violation)错误。

为什么选择地址 0 作为空指针?
  1. 地址 0 被保留:在大多数系统中,地址 0 通常不映射到任何实际的物理内存。将空指针定义为指向地址 0 可以确保在任何程序试图使用一个未初始化的指针时,都会触发错误。

  2. 便于检查:使用地址 0 作为空指针的标准,使得程序可以通过简单的检查 ptr == NULL 来判断指针是否有效。

  3. 硬件限制:早期的计算机系统(尤其是 16 位和 32 位架构)在设计时就规定了地址 0 不被用于普通的数据存储或代码,因此在这些系统中,空指针指向地址 0 成为了一种标准做法。

3. 空指针在操作系统中的处理

空指针本身并没有实际的内存位置(即它不映射到任何有效的内存块),但操作系统和硬件会特别处理试图访问空指针的情况:

  • 访问控制:现代操作系统通常会通过内存管理单元(MMU)来管理虚拟内存地址。当程序访问一个空指针时,它会试图访问地址 0。如果操作系统的虚拟内存管理设置了保护,访问地址 0 会导致一个 页错误(page fault)或 段错误

  • 段错误(Segmentation Fault):当程序试图访问空指针时,操作系统会触发一个段错误,表示程序尝试访问一个它无权访问的内存地址。这种错误通常会导致程序崩溃,并生成一个错误报告或堆栈跟踪信息。

4. 空指针的作用和使用场景

空指针通常用于以下几个场景:

  • 初始化指针:用来标识指针尚未指向任何有效内存。
  • 函数返回值:有时函数会返回一个空指针来表示错误或无法返回有效数据。例如,C 标准库中的 malloc 函数在内存分配失败时返回 NULL
  • 标记结束条件:例如,链表的尾节点的 next 指针通常指向 NULL,以标志链表的结束。

5. 总结

  • 空指针本质上是一个特殊的指针值,通常定义为 NULL,并在底层映射为 0x0(地址 0)。
  • 空指针并没有指向有效的内存地址,而是用来表示指针未初始化、无法访问有效数据,或者指向一个错误的内存区域。
  • 操作系统和硬件通过内存保护和错误检测机制(如段错误、访问违规)来处理对空指针的访问,防止程序继续运行或崩溃。
  • 由于空指针的特殊性,它在程序中被广泛用于表示无效的指针或作为函数返回值来指示错误或未找到数据。

在系统的设计中,空指针是一个非常有用且关键的概念,能够帮助开发者管理指针的生命周期和程序的内存访问安全。

二,复习怎么写链表

1,第一尝试

改了几个错误,

#include<iostream>struct ListNode
{int value;ListNode* next;
};ListNode* create_list_node(int num);
void printlist(ListNode*);int main()
{ListNode* head = create_list_node(5);printlist(head);return 0;
}ListNode* create_list_node(int num)
{//create the head node ListNode* head = (ListNode*)malloc(sizeof(ListNode));head->value = 0;head->next = NULL;//create a copy of the head nodeListNode* current = head;for (int i = 1; i < num + 1; i++){ListNode* p = (ListNode*)malloc(sizeof(ListNode));p->value = i;p->next = NULL;current->next = p;current = p;}return head;
}void printlist(ListNode* head)
{ListNode* current = head;while (current->next != NULL){std::cout << current->value << std::endl;current = current->next;}}

2,改进

修改后的代码

#include<iostream>struct ListNode
{int value;ListNode* next;
};ListNode* create_list_node(int num);
void printlist(ListNode*);
void deletenode(ListNode*);int main()
{ListNode* head = create_list_node(5);printlist(head);delete(head);return 0;
}//the definition of function
ListNode* create_list_node(int num)
{//create the head node ListNode* head = new ListNode;head->value = 0;head->next = NULL;//create a copy of the head nodeListNode* current = head;for (int i = 1; i < num + 1; i++){ListNode* p = new ListNode;p->value = i;p->next = NULL;current->next = p;current = p;}return head;
}void printlist(ListNode* head)
{ListNode* current = head;while (current != NULL){std::cout << current->value << std::endl;current = current->next;}}void deletenode(ListNode* head)
{ListNode* current = head;while (current != NULL){ListNode* nextNode = current->next;delete current;current = nextNode;}
}

增加了删除节点和使用new来分配内存空间。

三,反转链表

1,反转单链表

看完左神的视频后第一次尝试:

#include<iostream>struct ListNode
{int value;ListNode* next;
};ListNode* create_list_node(int num);
void printlist(ListNode*);
void deletenode(ListNode*);
ListNode* reverse_nodelist(ListNode*);int main()
{ListNode* head = create_list_node(5);printlist(head);std::cout << "the nodelist will be reversed !" << std::endl;head = reverse_nodelist(head);printlist(head);delete(head);return 0;
}//the definition of function
ListNode* create_list_node(int num)
{//create the head node ListNode* head = new ListNode;head->value = 0;head->next = NULL;//create a copy of the head nodeListNode* current = head;for (int i = 1; i < num + 1; i++){ListNode* p = new ListNode;p->value = i;p->next = NULL;current->next = p;current = p;}return head;
}void printlist(ListNode* head)
{ListNode* current = head;while (current != NULL){std::cout << current->value << std::endl;current = current->next;}}void deletenode(ListNode* head)
{ListNode* current = head;while (current != NULL){ListNode* nextNode = current->next;delete current;current = nextNode;}
}ListNode* reverse_nodelist(ListNode* head)
{ListNode* current = head;ListNode* pre = NULL;ListNode* next = NULL;while (current != NULL){next = current->next;current->next = pre;pre = current;current = next;}return pre;
}

2,改进

  1. 内存释放

    • 确保在 main 函数中调用 delete_list 来释放链表,而不是直接 delete head
  2. 代码优化

    • 重命名变量 pre 为 prev,更符合常见命名习惯。
    • 修改 deletenode 为更具语义的名称 delete_list
  3. 反转链表函数

    • 保持反转链表的代码简洁,命名清晰,功能完备。
  4. 内存管理

    • 使用 delete_list 来释放链表的内存,确保避免内存泄漏。

2,反转双链表

第一次尝试,

Double_node* reverse_nodelist(Double_node* head)
{Double_node* current = head, *prev = NULL, *next = NULL;current = head;while (current != NULL){prev = current->last;current->last = current->next;next = current->next;current->next = prev;current = next;}return prev;
}

会发现当current是空指针时,prev仍然指的是倒数第二个节点,无法返回头节点。

我看半天才看出来为什么没有输出结果,我问AI也没有给我说明白。就是current->next = current->last;

最初current是赋值为head,而head的last是NULL,所以导致后面的全是NULL,

Double_node* reverse_nodelist(Double_node* head)
{Double_node* current = head;Double_node* prev = NULL;Double_node* next = NULL;current = head;while (current != NULL){next = current->next;current->next = prev;current->last = next;prev = current;current = next;}return prev;
}

我感到很奇怪的一件事就是我的代码在VS上运行时就是没有翻转后的输出,但在其他DEV上就行。奇怪。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/468161.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Scrapy框架:Python爬虫开发快速入门与初试

在众多编程语言中&#xff0c;Python以其简洁的语法和强大的库支持&#xff0c;成为了编写爬虫的首选语言。而在Python的爬虫库中&#xff0c;Scrapy框架无疑是其中的佼佼者。Scrapy是一个开源的、基于Python的爬虫框架&#xff0c;它提供了一套完整的工具和功能&#xff0c;使…

C语言 | Leetcode C语言题解之第543题二叉树的直径

题目&#xff1a; 题解&#xff1a; typedef struct TreeNode Node;int method (Node* root, int* max) {if (root NULL) return 0;int left method (root->left, max);int right method (root->right, max);*max *max > (left right) ? *max : (left right);…

探索Python视频处理的瑞士军刀:ffmpeg-python库

文章目录 **探索Python视频处理的瑞士军刀&#xff1a;ffmpeg-python库**第一部分&#xff1a;背景介绍第二部分&#xff1a;ffmpeg-python库是什么&#xff1f;第三部分&#xff1a;如何安装ffmpeg-python库&#xff1f;第四部分&#xff1a;简单库函数使用方法1. 视频转码2. …

King3399(ubuntu文件系统)wifi设备树分析

该文章仅供参考&#xff0c;编写人不对任何实验设备、人员及测量结果负责&#xff01;&#xff01;&#xff01; 0 引言 文章主要介绍King3399(ubuntu)wifi设备树&#xff0c;涉及king-rk3399.dts、rp-wifi-sdio.dtsi内容修改与介绍 在使用wifi前本人遇到了一个比较奇怪的问…

Elmo驱动器上位机软件的详细配置

续接上文,本文讲解Elmo驱动器上位机软件更详细的配置,重点关注,在电机的位置受到约束的情况下,完成驱动器的参数整定过程,以及一些调试方法 一 硬件介绍 本文使用的是另一套设备,假设电机的位置是受到约束的 1 编码器规格书 编码器已知信息是 :读数头是26位的,通讯…

【Python】爬虫使用代理IP

1、代理池 IP 代理池可以理解为一个池子&#xff0c;里面装了很多代理IP。 池子里的IP是有生命周期的&#xff0c;它们将被定期验证&#xff0c;其中失效的将被从池子里面剔除池子里的ip是有补充渠道的&#xff0c;会有新的代理ip不断被加入池子中池子中的代理ip是可以被随机…

Ascend Extension for PyTorch是个what?

1 Ascend Extension for PyTorch Ascend Extension for PyTorch 插件是基于昇腾的深度学习适配框架&#xff0c;使昇腾NPU可以支持PyTorch框架&#xff0c;为PyTorch框架的使用者提供昇腾AI处理器的超强算力。 项目源码地址请参见Ascend/Pytorch。 昇腾为基于昇腾处理器和软…

【HarmonyOS Next】数据本地存储:@ohos.data.preferences

【HarmonyOS Next】数据本地存储&#xff1a;ohos.data.preferences 在开发现代应用程序时&#xff0c;数据存储是一个至关重要的过程。应用程序为了保持某些用户设置、应用状态以及其他小量数据信息通常需要一个可靠的本地存储解决方案。在 HarmonyOS Next 环境下&#xff0c…

数据结构——二叉树(续集)

♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥ ♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥ ♥♥♥我们一起努力成为更好的自己~♥♥♥ ♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥ ♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥ ✨✨✨✨✨✨个人…

MySQL性能测试方案设计

在现代互联网系统中&#xff0c;数据库性能直接影响到整体应用的速度和用户体验。而MySQL作为广泛使用的关系型数据库&#xff0c;随着数据量和并发请求的增长&#xff0c;其性能问题也日益突出。今天我们将深入探讨如何设计一套高效的MySQL性能测试方案&#xff0c;帮助你精准…

cv::intersectConvexConvex返回其中一个输入点集,两个点集不相交

问题&#xff1a;cv::intersectConvexConvex返回其中一个输入点集&#xff0c;但两个点集并不相交 版本&#xff1a;opencv 3.1.0 git上也有人反馈了intersectConvexConvex sometimes returning one of the input polygons in case of empty intersection #10044 是凸包嵌套判…

【学习笔记】SAP ABAP——内表

内表定义 ​ 内表是SAP ABAP中最具有影响力且最重要的功能之一&#xff0c;简而言之&#xff0c;用一句话概括内表的定义就是&#xff1a;***内表是可以在程序内部定义并且使用的表&#xff0c;属于本地表。***如下图展示出了参照数据库表sflight定义的内表的结构 内表与数据库…

MinerU容器构建教程

一、介绍 MinerU作为一款智能数据提取工具&#xff0c;其核心功能之一是处理PDF文档和网页内容&#xff0c;将其中的文本、图像、表格、公式等信息提取出来&#xff0c;并转换为易于阅读和编辑的格式&#xff08;如Markdown&#xff09;。在这个过程中&#xff0c;MinerU需要利…

[产品管理-66]:七步法创新工具:SCAMPER法,也被称为奔驰法,一种创新思考工具,帮助我们基于现有的产品找到产品创新突破的方向

SCAMPER法&#xff0c;也被称为奔驰法&#xff0c;是一种创新思考工具&#xff0c;由美国心理学家罗伯特艾伯尔&#xff08;也有说法是教育家和创新思考专家鲁伯特普里斯科特&#xff09;提出。这种检核表主要藉几个字的代号或缩写&#xff0c;代表七种改进或改变的方向&#x…

算法求解(C#)-- 寻找包含目标字符串的最短子串算法

1. 引言 在字符串处理中&#xff0c;我们经常需要从一个较长的字符串中找到包含特定目标字符串的最短子串。这个问题在文本搜索、基因序列分析等领域有着广泛的应用。本文将介绍一种高效的算法来解决这个问题。 2. 问题描述 给定一个源字符串 source 和一个目标字符串 targe…

IDEA启动提示Downloading pre-built shared indexes

Download pre-built shared indexes Reduce the indexing time and CPU load with pre-built JDK shared indexes 翻译&#xff1a; 下载预构建的共享索引 使用预构建的JDK共享索引减少索引时间和CPU负载. 使用预构建的JDK共享索引可以显著减少索引构建时间和CPU负载&#xf…

【DM系列】DM 集成 JDBC 开发指南

前言 数据库访问是数据库应用系统中非常重要的组成部分&#xff0c;DM 作为一个通用数据库管理系统&#xff0c;提供了多种数据库访问接口&#xff0c;包括 ODBC、JDBC、DPI 等方式。本开发指南详细介绍了 DM 的各种访问接口、相应开发环境的配置、以及一些开发用例。本指南的主…

处理PhotoShopCS5和CS6界面字体太小

处理PhotoShop CS6界面字体太小 背景&#xff1a;安装PhotoShop CS6后发现无法调大字体大小&#xff0c;特别是我的笔记本14寸的&#xff0c;显示的字体小到离谱。 百度好多什么降低该电脑分辨率&#xff0c;更改电脑的显示图标大小&#xff0c;或者PS里的首选项中的界面设置。…

【JavaEE进阶】Spring AOP 原理

在之前的博客中 【JavaEE进阶】Spring AOP使用篇_aop多个切点-CSDN博客 我们主要学习了SpringAOP的应用, 接下来我们来学习SpringAOP的原理, 也就是Spring是如何实现AOP的. SpringAOP 是基于动态代理来实现AOP的,咱们学习内容主要分以下两部分 1.代理模式 2.Spring AOP源码剖…

基于springboot+vu的二手车交易系统(全套)

一、系统架构 前端&#xff1a;vue | element-ui | html 后端&#xff1a;springboot | mybatis-plus 环境&#xff1a;jdk1.8 | mysql | maven | nodejs 二、代码及数据库 三、功能介绍 01. web端-首页1 02. web端-首页2 03. web端-注册 04. web端-登录 05. w…