当前位置: 首页 > news >正文

(delphi11最新学习资料) Object Pascal 学习笔记---第5章第5节(delphi中的指针)

5.5 什么是指针?

​ 指针是 Object Pascal 语言的另一种基本数据类型。一些面向对象的语言在很大程度上隐藏了指针这种强大但危险的语言结构,而 Object Pascal 则允许程序员在需要时使用指针(一般情况下并不经常使用)。

​ 那么什么是指针,这个名字又从何而来呢?与大多数其他数据类型不同的是,指针并不保存实际值,而是保存对变量的间接引用,而变量反过来又有一个值。一种更专业的表述方式是,指针类型定义了一个变量,该变量持有给定数据类型(或未定义类型)的另一个变量的内存地址。

注解:这是本书的一个高级章节,放在这里是因为指针是 Object Pascal 语言的一部分,应该成为任何开发人员的核心知识的一部分,尽管指针不是一个基础的主题,如果你是语言的新手,你可能想在第一次阅读本书时跳过这一部分。同样,你也有可能使用过没有(显式)指针的编程语言,所以这一小部分可能是一次有趣的阅读。

​ 指针类型的定义不是基于特定的关键字,而是使用一个特殊符号—插入符(^)。例如,你可以用下面的声明定义一个表示指向 Integer 类型变量的指针的类型:

typeTPointerToInt = ^Integer;

​ 一旦您定义了指针变量,可以使用 @ 运算符将另一个变量的地址进行赋值给指针变量:

varP: ^Integer;X: Integer;
beginX := 10;P := @X;// 使用指针更改X的值P^ := 20;Show('X: ' + X.ToString);Show('P^: ' + P^.ToString);Show('P: ' + UIntPtr(P).ToHexString(8));

​ 这段代码是 PointersTest 示例的一部分。在指针 P 指向变量 X 的情况下,您可以使用 P^ 指向变量的值,并读取或更改它。通过使用特殊类型 UIntPtr 将指针转换为数字,还可以显示指针本身的值,即 X 的内存地址(更多信息,请参阅下面的注释)。代码没有显示简单的数值,而是显示了十六进制表示法,这在内存地址中更为常见。这就是输出结果(指针地址可能取决于具体的编译):

X: 20
P^: 20
P: 0018FC18

警告:只有在限制为 2GB 的 32 位平台上,将指针转换为整数才是正确的。如果要使用更大的内存空间,就必须使用 Cardinal 类型。对于 64 位平台,更好的选择是使用 NativeUInt。不过,这种类型有一个别名,专门用于指针,称为 UIntPtr,它是这种情况下的最佳选择,因为使用它可以向开发人员和编译器清楚地表明你的意图。

为了清晰起见,让我总结一下。当您有一个指针 P 时:

  • 通过直接使用指针(使用表达式 P)可以引用指针所指向的内存的地址。
  • 通过解引用指针(使用表达式 P^)可以引用该内存位置的实际内容。

指针也可以不引用现有的内存位置,而是引用通过 New 过程在堆上动态分配的新的特定内存块。在这种情况下,当你不再需要指针访问的值时,你也必须通过调用 Dispose 来删除动态分配的内存。

注解:内存管理和堆的工作方式在第13章中有详细介绍。简而言之,堆是一块(很大的)内存区域,在堆中你不用按指定顺序分配和释放内存块。除了 New 和 Dispose 之外,还可以使用 GetMem 和 FreeMem,它们要求开发人员提供分配的大小(而在 New 和 Dispose 的情况下,编译器会自动确定分配的大小)。在编译时不知道分配大小的情况下、 GetMem 和 FreeMem 就变得非常方便。

下面是一段动态分配内存的代码片段:

varP: ^Integer;
begin// 初始化New(P);// 操作P^ := 20;Show(P^.ToString);// 终止Dispose(P);

如果在使用内存后为释放,程序最终可能会耗尽所有可用内存并崩溃。未释放不再需要的内存被称为内存泄漏。

警告:为了更安全起见,上面的代码实际上应该使用 try-finally 块,我决定在本书的这个部分不介绍这个主题,但我会在后面的第 9 章中介绍。

如果指针没有值,可以为其赋值为 nil。您可以通过直接相等测试或使用 Assigned 函数(如下所示)来测试指针是否为 nil,以确定它当前是否指向某个值。

这种测试经常使用,因为解引用无效指针会导致内存访问违规(根据操作系统的不同,影响也略有不同):

varP: ^Integer;
beginP := nil;Show(P^.ToString);

您可以通过运行PointersTest示例来查看代码的运行效果。您将看到的错误(在Windows上)应该类似于:

Access violation at address 0080B14E in module 'PointersTest.exe'. Read
of address 00000000.

使指针数据访问更安全的方法之一是添加 "指针不为空 "安全检查,例如下面的方法:

if P <> nil thenShow(P^.ToString);

正如我前面提到的,出于可读性的考虑,另一种通常更可取的方法是使用 Assigned 伪函数:

if Assigned(P) thenWriteln(P^.ToString);

注解: Assigned 并不是一个真正的函数,因为它是由编译器 "解析 "并生成正确代码的。此外,它还可以用于过程类型变量(或方法引用),而不实际调用它,只是检查它是否被赋值。

​ Object Pascal 还定义了指针数据类型,它表示无类型的指针(如 C 语言中的 void*)。如果使用无类型指针,则应使用 GetMem 而不是 New,并指出要分配的字节数,因为从类型本身无法推断出该值。每次分配的内存变量大小未定义时,都需要使用 GetMem 过程。

​ 在 Object Pascal 中很少需要指针,这是该语言的一个有趣的优点。不过,指针功能在实现一些极其高效的底层函数和调用操作系统的 API 时,还是有所帮助的。无论如何,了解指针对于高级编程和全面理解 Delphi 的对象模型(在幕后使用指针(通常称为引用))都非常重要。

警告: 当一个变量持有指向第二个变量的指针,而第二个变量离开作用域或被释放(如果是动态分配)时,指针将指向未定义或持有其他数据的内存位置。这会导致很难发现的错误。

相关文章:

  • [概念区分] 正则表达式与正则化
  • Llama中文大模型
  • mysql进阶学习 | DAY 14
  • UglifyJS 对 js 文件进行压缩
  • 【正则】正则表达式总结
  • [算法沉淀记录] 排序算法 —— 冒泡排序
  • LayUI发送Ajax请求
  • HarmonyOS—使用预览器查看应用/服务效果
  • Tomcat线程池原理(下篇:工作原理)
  • JAVA工程师面试专题-Mysql篇
  • 浅谈redis之SDS
  • 宝物筛选(二进制优化多重背包)
  • 数据结构与算法:图形数据结构
  • 解决弹性布局父元素设置高自动换行,子元素均分高度问题(align-content: flex-start)
  • 【思路】短链生成及访问
  • -------------------- 第二讲-------- 第一节------在此给出链表的基本操作
  • 【跃迁之路】【519天】程序员高效学习方法论探索系列(实验阶段276-2018.07.09)...
  • Docker 1.12实践:Docker Service、Stack与分布式应用捆绑包
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • emacs初体验
  • Intervention/image 图片处理扩展包的安装和使用
  • Iterator 和 for...of 循环
  • JS数组方法汇总
  • JS正则表达式精简教程(JavaScript RegExp 对象)
  • Koa2 之文件上传下载
  • nodejs调试方法
  • Node项目之评分系统(二)- 数据库设计
  • Object.assign方法不能实现深复制
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • SpiderData 2019年2月25日 DApp数据排行榜
  • swift基础之_对象 实例方法 对象方法。
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 盘点那些不知名却常用的 Git 操作
  • 如何设计一个比特币钱包服务
  • 少走弯路,给Java 1~5 年程序员的建议
  • 详解移动APP与web APP的区别
  • 小程序开发中的那些坑
  • 一些关于Rust在2019年的思考
  • 阿里云ACE认证之理解CDN技术
  • # 再次尝试 连接失败_无线WiFi无法连接到网络怎么办【解决方法】
  • (¥1011)-(一千零一拾一元整)输出
  • (6)添加vue-cookie
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (ZT)北大教授朱青生给学生的一封信:大学,更是一个科学的保证
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • .NET Conf 2023 回顾 – 庆祝社区、创新和 .NET 8 的发布
  • .NET Entity FrameWork 总结 ,在项目中用处个人感觉不大。适合初级用用,不涉及到与数据库通信。
  • .NET 反射的使用
  • .NET 中使用 TaskCompletionSource 作为线程同步互斥或异步操作的事件
  • .NET/ASP.NETMVC 深入剖析 Model元数据、HtmlHelper、自定义模板、模板的装饰者模式(二)...
  • @Transactional 详解
  • []C/C++读取串口接收到的数据程序
  • [20160807][系统设计的三次迭代]
  • [Android]使用Android打包Unity工程
  • [AS3]URLLoader+URLRequest+JPGEncoder实现BitmapData图片数据保存