C++—14、C++ 中的指针最基础的原理

devtools/2025/1/11 23:36:41/

       这里的指针是指原始指针,并非智能指针。计算机跟内存打交道,内存是计算机的一切,编程中最重要的东西可能就是内存。当你编写一个应用程序并启动它时,整个程序被载入内存,在你写的代码中,所有指令告诉计算机要做什么,所有这些被加载到内存中,这就是cpu如何访问你的程序并执行指令的。当你创建一个变量时,当你从硬盘中载入数据时,所有东西都会被存储到内存中,如果没有内存,你将什么也做不了。而指针对管理和操纵内存很重要。所以什么是指针?

一、指针的定义

指针是一个无符号整数(unsigned int),其值表示一个内存地址(其存储一个内存地址)。

因为我们代码所做的每一件事都是从内存中读取或者写入内存。内存可能是你拥有的至关重要的东西,你的电脑所能提供的重要的资源,所有的东西都依赖它,所以能够对这些内存有更多的控制至关重要。指针的类型与指针的值没有任何关系,类型只是我们虚构出来的,好让我们感觉轻松些,别管你有什么类型的指针,里面存放的都是个整数,都是个地址。

二、实例应用

新建项目,里面只有一个main.cpp文件。

这里新建个指针,void类型的,把变量var的地址存里面了。

再次强调,指针是没有什么类型的,里面存储的只是一个地址,一个整数,如果要是说有类型,只是指它所存储的地址所指的里面是存放什么类型的数据而已。

指针如果是空的话,我们可以这样写:

void*ptr=nullptr;

我们如上图所示F9来设置断点,按F5来调试程序。

当我们把鼠标悬停在ptr上时,显示一个地址0X0034FA58.我们把内存视图调出来

我们输入这个地址来看一看,这个地址中存放的是什么?

发现这个地址中正好存放的是8,变量var的值。

从这个实例中我们更加理解了,指针是个变量,是个存储地址的变量。

三、*指针

假设我想读取我的数据,我有指向那个数据的指针,现在我想写入或者读取这块数据,或者我想更改他,任何更改呢?

这时候逆向引用,就派上用场了,可以通过在指针前面加上个星号来实现。

*ptr,我实际上在逆向引用那个指针,也就是我现在在访问那块数据,我可以读取或者写入那块数据。例如我将值10写入。

如果这样做可能会产生错误,因为这里的类型指的是指针所指的地址的数据类型,为void。

测试下:

计算机如何从能把值写道一个void类型上去?这里的10,有可能是短整型2个字节,int是4个字节,long int是8个字节,它不知道该写入多少个字节的数据。其实这时应该用到类型了。程序修改如下:

我们来告诉编译器他就是个int类型,4个字节。记住是我们告诉编译器的,并不是编译器自己思考的。

我们来把这个值输出一下看看对不?

可以看到值已经被修改。

我们可以通过调试来观察内存的变化。

通过上面实例我们可以看出,通过星指针就是在访问那块数据,就可以写入那块内存。

现在对于指针的工作,大家应该很清楚了吧!

总结一下:指针就是指向内存中的一个位置。有些人说它指向一块内存,虽然不是很准确,因为我们不知道那块内存有多大。在上面这种情况下,我们知道,因为是int类型,是4个字节。

在实际的指针中并没有说内存有多大,当我们创建数组的时候,它有记录大小。确切的说它没有保存数据,我们不应该知道它有多大。

指针只是一个整数,也就是一个内存地址。

四、堆上内存分配

char是一个字节,这里我问他要的是8个字节的内存,并返回了指向这个内存开始地址的指针。然后我们可以使用memset的函数来填充这块内存,函数参数为指向这块地址开始地址的指针,然后是值,然后是大小就是需要填充多少字节。

调试运行

输入查询内存,发现

由于这里使用了new关键字,所以该数据是分配在heap上的

五、堆和栈

Heap 和 Stack 是计算机科学中两个基本的概念,它们在内存管理和数据结构方面扮演着重要的角色。以下是关于 Heap 和 Stack 的详细解释和比较。

堆(Heap)

定义:堆是一种内存区域,通常由程序员手动分配和释放内存。如果程序员不释放内存,程序结束时操作系统可能会回收这部分内存4。

特点

  • 手动管理:程序员需要显式地使用 malloc 或 new 等函数来分配内存,使用 free 或 delete 来释放内存21。
  • 动态大小:堆的大小在程序运行期间可以动态变化,没有固定的大小限制,但受到系统可用内存的限制2。
  • 内存碎片:频繁的内存分配和释放可能导致内存碎片,影响性能2。
  • 生长方向:堆的增长方向通常是向上的,即向内存地址增加的方向扩展2。

栈(Stack)

定义:栈是一种内存区域,由编译器自动分配和释放内存,通常用于存储函数的局部变量和参数4。

特点

  • 自动管理:编译器自动管理栈内存的分配和释放,不需要程序员显式地进行内存管理21。
  • 固定大小:栈的大小在程序开始时就已经确定,通常较小,受限于系统资源2。
  • 无内存碎片:由于栈采用先进后出(LIFO)的结构,不会产生内存碎片问题2。
  • 生长方向:栈的增长方向通常是向下的,即向内存地址减小的方向扩展2。

主要区别

特性Heap(堆)Stack(栈)
内存管理手动分配和释放自动分配和释放
内存大小动态大小,较大但有限固定大小,相对较小
内存碎片可能产生内存碎片不会产生内存碎片
生长方向向上,内存地址增加向下,内存地址减小
分配效率较低,涉及复杂算法较高,由硬件直接支持

 C++ 中,声明一个对象并在堆上分配内存的示例如下:

bject* p = new Object();

而在栈上分配内存的示例如下

Object obj;

六、指针的指针

如下:

注意这里说了需要手动释放,所以应该为:

设置断点在第8行,我们来调试一下:

我们在ptr的值输入内存:

这个值006fe648正好是buff的值,

而buff变量的值作为地址,里面放的是0.也就是这个指针变量放的是另一个指针的地址,另一个指针的地址指向一个内存空间的开始地址。


http://www.ppmy.cn/devtools/149719.html

相关文章

在 Ubuntu 上安装和配置 Redis

在 Ubuntu 上安装和配置 Redis,并使用发布-订阅(Pub/Sub)功能,可以按照以下步骤进行: 一、安装 Redis 1. 更新包列表 首先,更新本地的包列表以确保获取到最新的软件包信息: sudo apt update…

【漏洞分析】DDOS攻防分析

0x00 UDP攻击实例 2013年12月30日,网游界发生了一起“追杀”事件。事件的主角是PhantmL0rd(这名字一看就是个玩家)和黑客组织DERP Trolling。 PhantomL0rd,人称“鬼王”,本名James Varga,某专业游戏小组的…

城市生命线安全综合监管平台

【落地产品,有需要可留言联系,支持项目合作或源码合作】 一、建设背景 以关于城市安全的重要论述为建设纲要,聚焦城市安全重点领域,围绕燃气爆炸、城市内涝、地下管线交互风险、第三方施工破坏、供水爆管、桥梁坍塌、道路塌陷七…

学习、应用AI时,工作流与智能体的侧重选择

近日有朋友留言:“学习、应用AI时,工作流与智能体的侧重选择?” 其实在学习、应用AI时,选择工作流还是智能体主要取决于具体的应用场景和需求。以下是两者的一些特点和适用场景: 工作流的特点和适用场景 - 特点&…

ip属地出省会变吗?怎么出省让ip属地不变

在数字化时代,IP属地作为网络身份的一个重要标识,不仅影响着我们的网络体验,还与网络安全、隐私保护等方面息息相关。当我们跨省移动时,是否会遇到IP属地变化的问题?如果希望保持IP属地不变,又该如何操作呢…

Spring Boot项目中增加MQTT对接

在Spring Boot项目中增加MQTT对接,通常涉及以下几个步骤: 一、搭建MQTT服务器 首先,你需要搭建一个MQTT服务器(Broker)。这可以通过多种方式实现,例如使用Docker来部署EMQX或Mosquitto等MQTT Broker。 以…

C#用直线和曲线抗锯齿

使用 GDI 绘制一条线时,要提供线条的起点和终点,但不必提供有关线条上各个像素的任何信息。 GDI 与显示驱动程序软件协同工作,确定将打开哪些像素以在特定显示设备上显示该线条。 效果对比 代码实现 关键代码 e.Graphics.SmoothingMode Sm…

空对象模式

在空对象模式(Null Object Pattern)中,一个空对象取代 NULL 对象实例的检查。Null 对象不是检查空值,而是反应一个不做任何动作的关系。这样的 Null 对象也可以在数据不可用的时候提供默认的行为。 在空对象模式中,我…