一篇文章学懂C++和指针与链表

news2024/11/27 22:42:14

指针

目录

指针

C++的指针学习

指针的基本概念

指针变量的定义和使用

指针的所占的内存空间

空指针和野指针

const修饰指针

指针和数组

指针和函数

指针、数组、函数


 接下来让我们开始进入学习吧!

C++的指针学习

指针的基本概念

指针的作用:可以通过指针间接访问内存

  1. 内存编号是从0开始记录的,一般用十六进制数字表示

  2. 可以利用指针变量保存地址

这个概念和C语言是一模一样的,只要记住指针就是地址

指针变量的定义和使用

#include<iostream>
using namespace std;
int main()
{
    int a = 10;
    int *pa = &a;
    cout << a << endl;
    cout << pa << endl;
    cout << &a << endl;
    cout << *pa << endl;//可以通过解引用的方式来找到指针指向的内存
    *pa = 20;//通过指针来改变a的值
    cout << a << endl;
    cout << *pa << endl;
    return 0;
}
输出结果:
10
0x61fe14
0x61fe14
10
20
20

指针的所占的内存空间

指针也是一种数据类型,所以也会占内存空间。

#include<iostream>
using namespace std;
int main()
{
    int* pa;
    char* pb;
    short* pc;
    long* pd;
    float* pe;
    double* pf;
    cout << sizeof(pa) << endl;
    cout << sizeof(pb) << endl;
    cout << sizeof(pc) << endl;
    cout << sizeof(pd) << endl;
    cout << sizeof(pe) << endl;
    cout << sizeof(pf) << endl;
    return 0;
}
8
8
8
8
8
8

在32位的操作系统中,指针都是占4个字节的空间的。

在64位的操作系统中,指针则都是占8个字节的空间的。

所以看题目或者写代码的时候一定要清楚自己机器的大小和型号。

空指针和野指针

空指针:指针变量指向内存中编号为0的空间

用途:初始化针变量

注意:空指针指向的内存是不可以访间的

示例1:空指针

#include<iostream>
using namespace std;
int main()
{
    //空指针
    //空指针用于给指针变量进行初始化
    int* p = NULL;
    //2.空指针是不可以进行访问的,
    //0~255内存编号是系统占用的,使用会错误,因此不可访问
    cout << *p <<endl;
    return 0;
}

野指针:指针变量指向非法的内存空间

示例2:野指针

#include<iostream>
using namespace std;
int main()
{
    int* p = (int*)0x1100;
    cout << *p << endl;//这里还是无法输出,指向的是非法内存空间
    int arr[2] = { 1,2 };
    p = arr[2];//越界访问
    cout << *p <<endl;
    return 0;
}

总结:空指针和野指针都不是我们自己申请的空间,因此不要访问。

const修饰指针

const修饰指针的三种情况:

  1. const修饰指针——常量指针

  2. const修饰常量——指针常量

  3. const既修饰指针,又修饰常量

示例:

#include<iostream>
using namespace std;
int main()
{
    int a = 10;
    int b = 20;
    //常量变量
    //特点:指针的指向可以修改,但是指针指向的值不可以改
    const int* p1 = &a;
    a = 0;//*p1也会相应改变
    p1 = &b;//可以实现
    *p1 = 0;//不允许
    
    //指针常量
    //特点:指针的指向不可以修改,但是指针指向的值可以改
    int* const p2 = &a;
    *p2 = 10;
    p1 = &b;//不允许
    *p1 = 0;//可以实现
    
    //既修饰指针,又修饰常量
    //特点:指针的指向不可以修改,但是指针指向的值也不可以改
    const int* const p3 = &a;
    p3 = &b;//不允许
    *p3 = 0;//不允许
    return 0;
}

指针和数组

作用:利用指针访问数组中元素

一维数组

#include<iostream>
using namespace std;
int main()
{
    int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
    int * p = arr;
    for(int i = 0;i < 10; i++)
    {
        cout << *p + i << endl;//用p[i]也可以做到
    }
    return 0;
}

二维数组

#include<iostream>
using namespace std;
int main()
{
    int arr[2][3] = { { 0,1,2 },{ 3,4,5} };
    int (* parr)[3] = arr;
    for(int i = 0;i < 2; i++)
    {
        for(int j = 0;j < 3; j++)
        {
            cout << (*parr[i]+j)<<endl;
        }
    }
    return 0;
}

指针和函数

作用:利用指针作函数参数,可以修改实参的值

#include<iostream>
using namespace std;
void Swap(int* x,int* y)
{
    int tmp = *x;
    *x = *y;
    *y = tmp;
}
int main()
{
    //实现a,b交换
    int a,b;
    cin >> a >> b;
    Swap(&a,&b);
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    return 0;
}

指针、数组、函数

案例描述:封装一个函数,利用冒泡排序,实现对整型数组的升序排序 如数组:int arr[10]={4,3,6,9,1,2,10,8,7,5}

#include<iostream>
using namespace std;
​
void bubbleSort(int *p,int sz)
{
    for(int i = 0;i < sz - 1; i++)
    {
        for(int j = 0; j < sz - i - 1; j++)
        {
            //如果j>j+1的数据就要交换
            if(*(p + j) > *(p + j + 1))
            {
                int tmp = *(p + j);
                *(p + j) = *(p + j + 1);
                *(p + j + 1) = tmp;
            }
        }
    }
}
​
//打印数组
void printArray(int * p,int sz)
{
    for(int i = 0;i < sz; i++)
    {
        cout << *p + i << " ";
    }
}
int main()
{
    //1.创建数组
    int arr[10];
    for(int i = 0;i < 10; i++)
    {
        cin >> arr[i];
    }
    //数组长度
    int sz = sizeof(arr) / sizeof(arr[0]);
    //2.创建函数
    bubbleSort(arr,sz);
​
    //3.打印排序后的数组
    printArray(arr,sz);
    
    return 0;
}

链表

学习C++链表

链表的基本概念avi_

数据域1+next地址 ——> 数据域2+next地址 ——> 数据域3+next地址 ——> 数据域4+next地址 ——> 数据域5+next地址 ——> NULL(空)

链表是一种物理存储单元上非连续非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。

使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域空间开销比较大。链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序,数据的存取往往要在不同的排列顺序中转换。链表允许插入和移除表上任意位置上的节点但是不允许随机存取。链表有很多种不同的类型:单向链表,双向链表以及循环链表。链表可以在多种编程语言中实现。像Lisp和Scheme这样的语言的内建数据类型中就包含了链表的存取和操作。程序语言或面向对象语言,如C,C++和Java依靠易变工具来生成链表。

链表在指定位置插入和删除不需要移动元素,只需要修改指针即可。

查找效率低于数组

链表相对于数组而言,多了指针域空间开销

链表种类:

静态链表 动态链表 单向链表 双向链表 循环链表 单向循环链表 双向循环链表

拿到链表的第一个节点,就相当于拿到整个链表

头节点不保存任何数据

静态链表

 

静态链表:分配一整片连续的内存空间,各个结点集中安置,逻辑结构上相邻的数据元素,存储在指定的一块内存空间中,数据元素只允许在这块内存空间中随机存放,这样的存储结构生成的链表称为静态链表。也就是说静态链表是用数组来实现链式存储结构,静态链表实际上就是一个结构体数组

实现一个链表

#define _CRT_SECURE_NO_WARNING
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
​
//链表节点类型定义
struct LinkNode
{
    int data;
    struct LinkNode *next;
};
​
void test()
{
    struct LinkNode node1 = { 10, NULL };
    struct LinkNode node2 = { 20, NULL };
    struct LinkNode node3 = { 30, NULL };
    struct LinkNode node4 = { 40, NULL };
    struct LinkNode node5 = { 50, NULL };
    struct LinkNode node6 = { 60, NULL };
    
    node1.next = &node2;
    node2.next = &node3;
    node3.next = &node4;
    node4.next = &node5;
    node5.next = &node6;
    //连成链表
    
    //如何遍历这个链表
    struct LinkNode *pCurrent = &node1;
    while(pCurrent != NULL)
    {
        printf("%d ",pCurrent->data);
        //指针移动到下一个元素的首地址
        pCurrent = pCurrent->next;
    }
}
​
int main()
{
    test();
    return 0;
}

如何遍历这个链表 需要一个pCurrent指针能够指向下一个链表的地址

pCurrent = pCurrent->next

动态链表

这种链表在初始时不一定分配足够的空间, 但是在后续插入的时候需要动态申请存储空间,并且存储空间不一定连续, 在进行插入和删除时则不需要移动元素, 修改指针域即可,所以仍然具有链表的主要优点,链表结构可以是动态地分配存储的,即在需要时才开辟结点的存储空间,实现动态链接

链表的实现

LinkList.h

#define _CRT_SECURE_NO_WARNINGS
​
#pragma once
​
#include<stdio.h>
#include<stdnool.h>
#include<stdlib.h>
​
#iddef __cplusplus
#endif
    //定义节点数据类型
    struct LinkNode
    {
        int data;
        struct LinkNode *next;
    }
​
    //初始化链表
    struct LinkNode * Init_LinkList();
    //在值为oldval的后面插入一个新的数据newval
    void InsertByValue_LinkList(struct LinkNode *header,int oldval,int newval);
    //删除值为val的节点
    void RemoveByValue_LinkList(struct LinkNode *header,int delValue);
    //遍历
    void Foreach_LinkList(struct LinkNode *header);
    //销毁
    void Destroy_LinkList(struct LinkNode *header);
    //清空
    void Clear_LinkList(struct LinkNode *header);
    
#ifdef __cplusplus
#endif

LinkList.c

#include "LinkList.h"
//初始化链表
struct LinkNode * Init_LinkList()
{
    //创建头节点
    struct LinkNode *header = malloc(sizeof(struct LinkNode));
    header->data = -1;
    header->next = NULL;
    
    //尾部指针
    struct LinkNode *pRear = header;
    int val = -1;
    while(true)
    {
        printf("输入插入的数据:\n");
        scanf("%d",&val);
        if(val == -1)
        {
            break;
        }
        
        //先创建新节点
        struct LinkNode *newnode = malloc(sizeof(struct LinkNode));
        newnode->data = val;
        newnode->next = NULL;
        
        //新节点插入到链表中
        pRear->next = newnode;
        
        //更新尾部指针指向
        pRear = newnode;
    }
    return header;
}
​
    //在值为oldval的位置插入一个新的数据newval
    void InsertByValue_LinkList(struct LinkNode *header,int oldval,int newval)
    {
        if(NULL == header)
        {
            return;
        }
        //两个辅助指针变量
        struct LinkNode *pPrev = header;
        struct LinkNode *pCurrent = pPrev->next;
        while(*pCurrent != NULL)
        {
            if(pCurrent->data == oldval)
            {
                break;
            }
            pPrev = pCurrent;
            pCurrent = pCurrent->next;
        }
        
        //pCurrent为空那就说明链表中不存在职位oldval的节点
        if(pCurrent == NULL)
        {
            return;
        }
        //先创建新节点
        struct LinkNode *newnode = malloc(sizeof(struct LinkNode));
        newnode->data = newval;
        newnode->next = NULL;
        //新节点插入到链表中
        pPrev->next = newnode;
        newnode->next = pCurrent;
    //删除值为val的节点
    void RemoveByValue_LinkList(struct LinkNode *header,int delValue)
    {
        if(NULL == header)
        {
            return;
        }
        //两个辅助指针变量
        struct LinkNode *pRrev = header;
        struct LinkNode *pCurrent = pRrev->next;
        //
        while(pCurrent != NULL)
        {
            if(pCurrent->data == delValue)
            {
                break;
            }
            //移动两个辅助指针
            pPrev = pCurrent;
            pCurrent = pCurrent->next;
        }
        if(pCurrent == NULL)
        {
            return;
        }
        //重新建立待删除节点的前驱和后继节点关系
        pPrev->next = pCurrent->next;
        free(pCurrent);
        pCurrent == NULL;
    }
​
    //遍历
    void Foreach_LinkList(struct LinkNode *header)
    {
        if(NULL == header)
        {
            return;
        }
        //辅助指针变量
        struct LinkNode *pCurrent = header->next;
        
        while(pCurrent != Null)
        {
            printf("%d ",pCurrent->data);
            pCurrent = pCurrent->next;
        }
    }
​
    //销毁
    void Destroy_LinkList(struct LinkNode *header)
    {
        if(NULL == header)
        {
            return;
        }
        //辅助指针变量
        struct LinkNode *pCurrent = header;
        
        while(pCurrent != NULL)
        {
            //先保存当前节点的下一个节点地址
            struct LinkNode *pNext = pCurrent->next;
            //释放当前节点内存
            free(pCurrent);
            //指针向后移动
            pCurrent = pNext;
        }
    }
    //清空
    void Clear_LinkList(struct LinkNode *header)
    {
        if(NULL == header)
        {
            return;
        }
        //辅助指针变量
        struct LinkNode *pCurrent = header->next;
        while(pCurrent != NULL)
        {
            //先保存当前节点的下一个节点位置
            struct LinkNode *pNext = pCurrent->next;
            
            //释放当前节点内存
            free(pCurrent);
            
            //pCurrent指向下一个节点
            pCurrent = pNext;
        }
        header->next = NULL;
    }

TestLinkList.c

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

K8S集群管理平台Rancher(2):安装与使用

Rancher 是一个容器管理平台。Rancher 简化了使用 Kubernetes 的流程。 下面记录一下手动安装Rancher的步骤 一、docker安装rancher 拉取rancher镜像 docker pull rancher/rancher运行rancher容器 sudo docker run -d --restartalways \ -v /mydata/docker/rancher_data…

基于Apache Maven构建多模块项目

title: 基于Apache Maven构建多模块项目 date: 2022-04-10 00:00:00 tags: Apache Maven多模块 categories:Maven 介绍 多模块项目由管理一组子模块的聚合器 POM 来构建。在大多数情况下聚合器位于项目的根目录中&#xff0c;并且必须是 pom 类型的项目。子模块是常规的 Mave…

excel数据整理:合并计算快速查看人员变动

相信大家平时在整理数据时&#xff0c;都会对比数据是否有重复的地方&#xff0c;或者该数据与源数据相比是否有增加或者减少。数据量不大还好&#xff0c;数据量大的话&#xff0c;对比就比较费劲了。接下来我们将进入数据对比系列课程的学习。该系列一共有两篇教程&#xff0…

ESP8266 + STC15+ I2C OLED带网络校时功能的定时器时钟

ESP8266 + STC15+ I2C OLED带网络校时功能的定时器时钟 📍相关篇《ESP8266 + STC15基于AT指令通过TCP通讯协议获取时间》 📌ESP8266 AT固件基于安信可AT固件,相关刷AT固件可以参考《NodeMCU-刷写AT固件》 🔖STC15 单片机采用的是:STC15F2K60S2 晶振频率采用内部:22.11…

Java面试——多线程并发篇

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

滑动窗口算法

滑动窗口 滑动窗口有俩种&#xff1a;定窗口和不定窗口。滑动窗口说白了就是双指针的运用。 定窗口说明是一个特定窗口大小&#xff0c;通常用来解决相邻的元素&#xff0c;最大值&#xff0c;最小值。 不定窗口说的是&#xff0c;先由右指针去找到第一个能够符合题意的位置…

【微信小程序】在WXML文件中显示JS文件中全局变量的方法

前言我们知道在wxml中可以通过数据绑定的方法来获取到js文件中data里面的数据&#xff0c;并且显示到wxml界面&#xff0c;那么我们该如何在wxml中显示js文件里面的全局变量呢&#xff1f;显示data里面数据的方法在wxml种我们可以显示js代码中data代码段中的变量。具体的操作是…

《跃迁:从技术到管理的硅谷路径》读后感

一、技术管理 1.技术管理包含两层含义&#xff1a; 一层是管理自己和团队的技术&#xff0c;进行技术选型&#xff0c;在正确的场景使用最适合的技术&#xff0c;保证程序简捷、强壮、可维护&#xff0c;最终完成产品的上线另一层是管理技术团队&#xff0c;帮助团队成员成长…

【面试题】2023 vue高频面试知识点汇总

一、MVVM原理在Vue2官方文档中没有找到Vue是MVVM的直接证据&#xff0c;但文档有提到&#xff1a;虽然没有完全遵循MVVM模型&#xff0c;但是 Vue 的设计也受到了它的启发&#xff0c;因此在文档中经常会使用vm(ViewModel 的缩写) 这个变量名表示 Vue 实例。为了感受MVVM模型的…

十二款硬盘数据恢复软件!恢复数据,最适合你的是这一款

硬盘数据发生丢失&#xff0c;通过快捷键、回收站、备份都无法恢复回来。对于电脑小白来说&#xff0c;最好的方法就是通过专业软件恢复数据。市面上很多硬盘数据恢复软件&#xff0c;我们大多不清楚它们有哪些功能&#xff0c;好不好用。 为此&#xff0c;我尝试了十二款数据…

雪花算法snowflake

snowflake中文的意思是 雪花&#xff0c;雪片&#xff0c;所以翻译成雪花算法。它最早是twitter内部使用的分布式环境下的唯一ID生成算法。在2014年开源。雪花算法产生的背景当然是twitter高并发环境下对唯一ID生成的需求&#xff0c;得益于twitter内部高超的技术&#xff0c;雪…

【从头开始】Java基础Day05

面向对象、三大特性、异常处理Day05面向对象构造器创建对象内存分析封装继承Super关键字重写多态instanceofStatic详解抽象类接口异常ErrorExceptionError和Exception的区别异常处理机制自定义异常类实际应用中关于异常的总结Day05 面向对象 面向对象编程的本质就是&#xff1…

在这些工厂、农田、服务区,看到智能中国的草蛇灰线

今天&#xff0c;应该很少人会怀疑&#xff0c;以AI、5G、云计算等新一代技术为核心的第四次工业革命&#xff0c;将发生在中国。正如保尔芒图在《十八世纪产业革命》中写道的那样&#xff1a;大工业的特殊性&#xff0c;并不是一下子就显露出来的。一开始&#xff0c;只是城市…

如何将VirtualBox虚拟机转换到VMware中

转换前的准备 首先需要你找到你的virtualbox以及VM安装到哪个文件夹里了&#xff0c;需要将这两个文件夹添加进环境变量Path中。 如果你记不清了&#xff0c;可以用everything全局搜索一下“VBoxManage.exe’以及“vmware-vdiskmanager.exe”&#xff0c;看一眼这个程序放到哪…

推荐几个好用的在线代码编译器

程序员最喜欢用的在线IDE代码编译器&#xff0c;功能很强大&#xff0c;类别很全&#xff01; 1.网址https://tech.io/snippet 支持 20 种编程语言&#xff0c;页面上没有杂七杂八的东西&#xff0c;非常简约&#xff0c;非常干净&#xff0c;另外&#xff0c;它上面的代码段…

UE4:使用样条生成随机路径,并使物体沿着路径行走

一、关于样条的相关知识 参考自&#xff1a;样条函数 - 馒头and花卷 - 博客园 三次样条&#xff08;cubic spline&#xff09;插值 - 知乎 B-Spline(三)样条曲线的性质 - Fun With GeometryFun With Geometry 个人理解的也不是非常深&#xff0c;但是大概要知道的就是样条具…

【C++实现红黑树】

文章目录一、红黑树的概念及性质二、红黑树节点的定义三、红黑树的插入四、红黑树的验证五、完整代码六、总结一、红黑树的概念及性质 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或Black。 通过对任何一…

时间短/背景弱/获两国邀请函如期赴日本工程院士麾下访学

K老师因省公派要求&#xff0c;年底必须出国&#xff0c;除了申请还要办理各种派出手续&#xff0c;时间很紧张。本人为讲师&#xff0c;硕士学位&#xff0c;且没有论文及课题项目作为依托&#xff0c;学术背景较弱。通过努力&#xff0c;我们分别获得了美国夏威夷大学马诺阿分…

花了5年时间,用过市面上95%的工具,终于找到这款万能报表工具

经常有粉丝问我有“哪个报表工具好用易上手&#xff1f;”或者是“有哪些适合绝大多数普通职场人的万能报表工具&#xff1f;” 从这里我大概总结出了大家选择报表工具最期望满足的3点&#xff1a; &#xff08;1&#xff09;简单易上手&#xff1a;也就是所谓的学习门槛要低…

如何下载高程DEM

同步视频教程&#xff1a;卫星地图_高清卫星地图_卫星地图视频_下载高程等高线使用视频教程专题地图制作视频教程&#xff1a;卫星地图_高清卫星地图_卫星地图视频_地图数据应用&#xff08;制作地图效果的基本过程&#xff09;视频教程&#xff1a;如何选择中央子午线或者分度…