P7 C++指针

前言 

指针是一个令很多人都很痛苦的内容,然而指针其实没有大家想象中的那么复杂。

对计算机来说内存就是一切,如果非要我说出编程中最重要的一件事,我可能会说是内存

当你编写了一段程序并启动它时,所有的程序都被载入到内存中,指令告诉计算机在你写的代码中要做什么。所有这些都被加载到内存中,CPU 就是这样访问你的程序并执行它的指令的。

当你创建一个变量,并从磁盘中读取数据时,所有的这些都存储在内存中,如果没有内存就什么也做不了。而指针对于 管理 和 操纵内存 非常重要。

01 指针的概念

其实,指针是一个整数,一种存储内存地址的数字,指针的值就是一串数字,而这数字其实就是一个地址,仅此而已

内存它就像一条很长的直线而不是一大块。内存就像我们现实世界中的一条街,这一条街有开始也有结束,就像一根线一样,线上就是一堆房子, 没有房子是横穿街道的,假设只有这一条街道,一排的房子,我们现在把这个比喻用在电脑上,它只是一条线性的线,在这条直线上的每一所房子都有一个号码和一个空间,号码就是地址,空间是一个字节,我们显然需要一种方法来寻址所有的 byte 来定位我们这条街上所有的房子。

例如,假设某人在网上订了东西想要送货上门,他需要被送到正确的房子里,或者可能有人把东西从他们的房子里取出去,无论哪种操作,你需要能够从这些房子的内存字节中读写,指针就是这些地址,这些地址告诉我们房子在哪里,这是非常重要的,因为我们在代码中所做的几乎所有的事情都是在从内存中读写,当然你完全有可能写一个不使用指针的 C++ 程序,你完全可以这样做。然而指针是非常有用的工具,正如我刚才提到的,内存可能是你拥有的最重要的东西,是计算机可以提供的很重要的资源,它可以被用于做几乎所有的事情,能够对内存有更多的控制至关重要。

再次重申,一个指针只是一个地址,它是一个保存内存地址得到整数虚构

02 第一个指针

我们来创建一个空指针,void 的意思是无类型。

千万记住,一个指针只是一个地址,它只是一个在内存中保存地址的整数,它不需要类型,如果我们给指针一个类型,我们只是说,这个地址的数据,被假设为我们给的类型,除此之外它没有任何意义,它只是一些我们在实际的源代码可以编写的东西,使我们的生活在语法层面上更容易,为了让我们的生活更轻松。

我们当然可以使用指针类型,不过类型不会改变一个指针的实质,——指针只是一个内存地址,它是一个整数。

所以 void 指针 意味看我们现在不关心我们的代码中这个类型是什么类型的,因为我们只想保存一个地址。

我把它称为 pdata,其值设置为0。0 是什么意思?我们给这个指针的内存地址是 0,这是什么意思?

0 实际上不是一个有效的内存地址,内存地址不会一直到 0,这是无效的,这意味着这个指针是无效的,无效指针是完全可以接受的状态,但我要说的是 0 不是一个有效的内存地址,我们不能从内存地址 0 中读取或写入数据,如果我们尝试这样做的话,程序会崩溃,所以 0 意味着没有。

我们也可以这样写。

这种写法实际上是一个 #define,你把鼠标悬浮在上面可以看到,是 #define NULL 0,和我们用 0 是一样的。或者我们也可以用 C++ 关键字 nullptr,这个会在 C++11 里面介绍。

好的,我们设置了我们的第一个指针,它是无类型的,它的内存地址是 0,一点用处没有,但它可能是你能写的最简单一个指针,这可以让我们做一些更有用的事情。

对上面的代码我们做一些解释。

我们创造一个整数变量,当然我们创建的每个变量都有一个内存地址,因为我们需要一个地方来存储这个变量。

如果我想知道这个变量的内存地址,我可以通过使用 & 运算符来做到这一点,如果我在一个已经存在的变量前面加上一个 & 符号,我们实际上是在问这个变量,嘿,你的内存地址是什么?

我们取这个变量的内存地址,把它赋值给指针变量 pdata。

我们现在有了变量 num的内存地址,我们把它存储在另一个变量中。

我们设置一个断点调试一下。


我们可以看变量的值,当代码执行到下面两句时,在下图左框2pdata的值为0x7fffffffdd0c,其实这是一个整形数,不过这个数字代表着存放变量num的值,所以我们查看0x7fffffffdd0c这个地址时,你会发现这个地址的值是num变量的值

    int num = 9;
    pdata = #

将 void 修改为 int 并运行,你会发现实际上没有改变任何变化。(自己去试一下)

Ubuntu 1.84.2Visual Studio Code 下载配置与vscode查看内存Hex Editor插件,简单易懂-CSDN博客

如果你的VScode还不能查看内存,请看上篇博文

03 如何访问指针保存的地址

我们再做一下修改。

假设我想使用我的数据,我有一个指针指向那个数据,现在我想要写入或读取数据,我该如何操作呢?换句话说,我们知道数据在哪里,但是我怎么能访问它呢,这就要靠逆向引用了(指针的 * 运算符通常被称为 dereference 运算符)。

我们有变量 num,指针 pdata指向 num,但是我怎么才能回到这个 num呢? 你可以通过在指针前面插入一个星号来实现这一点,换句话说,我实际上是在逆向引用那个指针,这意味着我现在可以访问我可以读取或写入数据的数据。我们试着这样做一下。

04 表达式必须是指向完整对象类型的指针C/C++(8

因为我们说过这个指针是一个空指针,也就是说,计算机怎么可能将这个值写入到一个 void 指针,它不知道那是什么,这个 10 是 short 类型吗?——两个字节的整数,是 int 类型吗?——四个字节的整数,是 long long 类型吗?—— 8 个字节的整数,它不知道这需要多少字节的数据,我们刚刚说它是 10,但是 10 可以代表任何东西,这个时候就需要类型了,我们需要告诉编译器,这是一个整数,所以是 4 个字节,我们修改一下。

当然,是我们告诉编译器,这是一个整数,编译器自己并不知道这是不是正确的,如果我们犯了错,比如我们说这实际是一个 double,那程序的运行可能就有点麻烦了。

好吧,通过写代码的时候,逆向引用 *指针,我可以访问这个数据,这个例子中,我写入这个数据。

简单地说,我们不知道指针有多大,我们不知道指针指向的数据多大,因为指针并不包含数据,一个指针就是一个整数,它是一个内存地址,就是这样。

 05 不能将 "int *" 类型的值分配到 "short *" 类型的实体

当我将指针pdata的类型改为short *时,编译器会报错,因为不能将 "int *" 类型的值分配到 "short *" 类型的实体,如果我不想改变指针的类型的话,有没有办法可以解决这个问题,答案是可以的,将num的类型直接强转就行

这样就可以解决

06 new & delete

到目前为止,我们一直在栈上直接创建数据,如果我们像上面的例子一样操作,那就是在栈中创建变量(之后我们会讲到栈和堆的内容)。

如果我想在堆上创建一个变量,或许我可以问我们的电脑,嘿,我想让你给我分配一些内存,我想有一定的尺寸(比如 8 个字节),我会这样做。

#include <iostream>
#include <string.h>
int main()
{
    int *pdata = NULL;
    int num = 9;
    pdata =&num;
    *pdata = 10;
    char  *buf= new  char[8];
    memset(buf, 1, 8);
    return 0;
}

上面的代码给我们分配了 8 个字节的内存,并返回一个指向那块内存开始位置的指针,然后我可以使用 memset 的函数,它可以用我们指定的数据填充一段内存块。

memset 接收一个指针,这个指针将会是内存块的开始的指针,然后是将要填入的值,比如 0,最后是应该填入多少字节,我们要 8 个字节。

运行这个程序。在内存视窗可以看到 buffer 位置的连续 8 个字节都为1 。

这个例子中,我们使用了新的关键字 new 申请了堆内存,当我们完成它后,我们也应该删除数据。

我们可以通过键入 delete 完成删除,我们知道它是一个数组,我们使用数组来分配堆内存,所以我们应该使用 delete[ ] 来删除 buf。

这个例子再次强调了,这个指针,我们分配 8 个 char,1 个 char 是一个字节,这样我们就分配了 8 个字节, 我们用来存储数据的指针指向了数据的开头。

07 指针的指针

还有一点我想说的是指针本身是变量,这些变量也存储在内存中,这意味着我们可以得到双指针或三指针,意思可以有指向指针的的指针。这一切可以如何运作呢,好吧,你只要往下一层想,我现在有一个指针指向我的指针,于是我有了一个变量 a 来存储内存地址,它指向另一个变量 b,变量存储变量 c 的内存地址。就这么简单。

在 buffer 的例子中,我们可以创建一个双指针试一下。

#include <iostream>
#include <string.h>
int main()
{
    int *pdata = NULL;
    int num = 9;
    pdata =&num;
    int **pdata1 = &pdata;
    return 0;
}

我回到指针上来,再次强调,它只是存储内存地址的整数 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

IDEA必备插件!一键生成接口文档

IDEA是一款功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它可以帮助开发人员更加高效地编写、调试和部署软件应用程序。我们在编写完接口代码后需要进行接口调试等操作&#xff0c;一般需要打开额外的调试工具&#xff0c;而今天给大家介绍一款IDEA插件&…

网站定制开发主要分类有哪些|企业 app 软件小程序定制

网站定制开发主要分类有哪些|企业 app 软件小程序定制 网站定制开发是指根据客户需求&#xff0c;为其量身定制设计和开发的网站服务。目前&#xff0c;网站定制开发主要分为以下几个分类&#xff1a; 1.静态网站定制开发&#xff1a;静态网站是由 HTML、CSS 和 JavaScript 等静…

什么是巧克力葡萄酒,值得一试吗?

许多葡萄酒爱好者喜欢浓郁巧克力味的红酒&#xff0c;巧克力葡萄酒是由葡萄酒和液体巧克力制成的混合饮料&#xff0c;它质地厚实&#xff0c;非常甜&#xff0c;带有红色水果、焦糖或咖啡的香味。它可能会让你想起可可饮料&#xff0c;而不是葡萄酒饮料。只有真正含有巧克力成…

vscode项目推送到git

1、打开项目文件 打开文件后点击vs code左侧工具栏中第三个源代码管理图标&#xff0c;点击初始化仓库&#xff0c;此时会创建一个本地仓库会检查该项目中的文件变更 2、创建远程仓库 点击克隆/下载&#xff0c;复制HTTPS地址 3、添加远程地址 1&#xff09;图形化操作 2…

【开源】基于Vue.js的农村物流配送系统的设计和实现

项目编号&#xff1a; S 024 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S024&#xff0c;文末获取源码。} 项目编号&#xff1a;S024&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统登录、注册界面2.2 系统功能2.2…

uni微信小程序 map 添加padding

问题背景&#xff1a; 规划驾车线路的时候&#xff0c;使用uni的include-points指定可视范围的时候&#xff0c;会很极限。导致marker不能完全显示。 解决方法 给地图显示范围添加padding (推荐) <mapid"myMap":markers"markers":polyline"pol…

【LeetCode:1410. HTML 实体解析器 | 模拟+哈希表+字符串+库函数】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

设计模式—开闭原则

1.背景 伯特兰迈耶一般被认为是最早提出开闭原则这一术语的人&#xff0c;在他1988年发行的《面向对象软件构造》中给出。这一想法认为一旦完成&#xff0c;一个类的实现只应该因错误而修改&#xff0c;新的或者改变的特性应该通过新建不同的类实现。新建的类可以通过继承的方…

Spring Cloud 版本升级遇坑记:OpenFeignClient与Gateway的恩怨情仇

Spring Cloud 版本升级遇坑记&#xff1a;OpenFeignClient与Gateway的恩怨情仇 近日&#xff0c;在对项目中的 Spring Boot、Spring Cloud 以及 Spring Cloud Alibaba 进行版本升级时&#xff0c;遭遇了一个令人头疼的问题&#xff1a;Spring Cloud Gateway 在运行时一直卡住&a…

Python web自动化测试 —— 文件上传

​文件上传三种方式&#xff1a; &#xff08;一&#xff09;查看元素标签&#xff0c;如果是input&#xff0c;则可以参照文本框输入的形式进行文件上传 方法&#xff1a;和用户输入是一样的&#xff0c;使用send_keys 步骤&#xff1a;1、找到定位元素&#xff0c;2&#…

CV计算机视觉每日开源代码Paper with code速览-2023.11.20

点击CV计算机视觉&#xff0c;关注更多CV干货 论文已打包&#xff0c;点击进入—>下载界面 点击加入—>CV计算机视觉交流群 1.【人脸识别】FRCSyn Challenge at WACV 2024:Face Recognition Challenge in the Era of Synthetic Data 论文地址&#xff1a;https://arxi…

centos系统下,docker安装sqlserver并用本地Navicat连接

文章目录 一&#xff0c;centos下安装docker二&#xff0c;docker安装sqlserver20192.1 安装遇到的问题2.1.1 修改用户名进不去数据库2.1.2 安装2022版的sqlserver发现启动失败 三&#xff0c;Navicat连接centos下的sqlserver3.1 下载ODBC Driver 参考微软网址&#xff1a; 使…

SUSE 15.2升级Openssh9.5

SUSE 15.2升级Openssh9.5 公司有部分SAP的机器用的SUSE,懒得弄rpm包了.直接编译安装 1. 添加阿里云源 zypper addrepo -f http://mirrors.aliyun.com/opensuse/distribution/leap/15.2/repo/oss/ openSUSE-15.2-Oss zypper addrepo -f http://mirrors.aliyun.com/opensuse/d…

Vatee万腾科技新高峰:Vatee前瞻性创新的数字化之力

Vatee万腾科技&#xff0c;一家以前瞻性创新为核心驱动力的数字化引领者&#xff0c;正迈向新的高峰。其在科技领域的卓越表现不仅体现在技术实力上&#xff0c;更展现在对未来的深刻洞察和独到思考上。 在Vatee的科技舞台上&#xff0c;前瞻性创新如一道独特的光芒&#xff0c…

Visual Components软件典型功能描述 衡祖仿真

1、即点即用&#xff0c;即插即用 vc提供大量的组件模块&#xff0c;组件都已经赋子行为和渲染&#xff0c;看起来复杂的模拟场景,可以通过简单拖拉组合&#xff0c;即可成为一条运动的仿真。节省更多的时间&#xff0c;让布局更灵动。 2、PLC功能 过去&#xff0c;PLC程序的…

视频服务网关的三大部署(三)

视频网关是软硬一体的一款产品&#xff0c;可提供多协议&#xff08;RTSP/ONVIF/GB28181/海康ISUP/EHOME/大华、海康SDK等&#xff09;的设备视频接入、采集、处理、存储和分发等服务&#xff0c; 配合视频网关云管理平台&#xff0c;可广泛应用于安防监控、智能检测、智慧园区…

2023年面试测试工程师一般问什么问题?

面试和项目一起&#xff0c;是自学路上的两大拦路虎。面试测试工程师一般会被问什么问题&#xff0c;总结下来一般是下面这4类&#xff1a; 1.做好自我介绍 2.项目相关问题 3.技术相关问题 4.人事相关问题 接下来&#xff0c;主要从以上四个方向分别展开介绍。为了让大家更有获…

现货黄金区间交易的两个要点

在现货黄金市场中&#xff0c;我们常碰到横盘区间行情。有区间&#xff0c;就终究会出现突破&#xff0c;因为金价不可能缺乏方向而一直在区间内运行。那既然要突破&#xff0c;我们又应当如何应对和交易呢&#xff1f;下面我们就来讨论一下。 切忌在突破发生时马上跟随突破方向…

[HCIE] IPSec-VPN (手工模式)

概念&#xff1a; A. IPSec&#xff1a;是对IP的安全性补充&#xff0c;工作在IP层&#xff0c;为IP网络通信提供安全服务。 B.安全联盟SA&#xff1a;是通信对等体之间对某些要素的协定。 C. IPSec安全联盟简称 IPSec SA.通常成对建立&#xff08;inbound和outbound&#x…

vue怎么实现国际化? vue-i18n 插件实现国际化,支持切换不同语言

依赖的文档开始 | Vue I18n 一、安装 npm install vue-i18n 如果在一个模块系统中使用它&#xff0c;你必须通过 Vue.use() 明确地安装 vue-i18n&#xff1a; import Vue from vue import VueI18n from vue-i18nVue.use(VueI18n)二、使用 在 src 下创建 lang 文件夹 1.准…