C++系列第九篇 数据类型下篇 - 复合类型(指针高级应用)

 系列文章

C++ 系列 前篇 为什么学习C++ 及学习计划-CSDN博客

C++ 系列 第一篇 开发环境搭建(WSL 方向)-CSDN博客

C++ 系列 第二篇 你真的了解C++吗?本篇带你走进C++的世界-CSDN博客

C++ 系列 第三篇 C++程序的基本结构-CSDN博客

C++ 系列 第四篇 C++ 数据类型上篇—基本类型-CSDN博客

C++ 系列 第五篇 C++ 算术运算符及类型转换-CSDN博客

C++系列第六篇 数据类型下篇 - 复合类型(数组及字符串)-CSDN博客

C++系列第七篇 数据类型下篇 - 复合类型(结构体、共用体及枚举)-CSDN博客

C++系列第八篇 数据类型下篇 - 复合类型(指针及动态内存申请)-CSDN博客

前言  

        这一章节进行复合类型最后一部分的介绍,主要是指针的一些高级应用,包括指针和数组的的关系,指针和字符串的关系,指针和结构的关系,  变量基于内存位置进行的分类。 还会简单介绍下 C++特有的动态数组,vector 和 array。

指针和数组 

        进行过C编程的人一定进行过指针和数组等价使用,即申请的是数组,按指针操作,或者一个指针指向一块申请的内存,按数组进行操作。 C++中指针和数组也可以进行等价使用。指针和数组基本等价的原因在于指针算术和C++内部处理数组的方式。

        首先,将整数变量加 1 后,其值将增加1:但将指针变量加1 后,增加的量等于它指向的类型的字节数。将指向 double 的指针加1 后,如果系统对 double 使用 8 个字节存储,则数值将增加 8;将指向short 的指针加1 后,如果系统对 short 使用 2 个字节存储,则指针值将增加 2。

        如上的示例除了说明指针的数值计算外,还有一个比较重要的点就是将数组名解释为地址。大部分情况两者可以等价操作,可以像示例中一样使用指针的数值计算一样进行数组的数值计算,同时也能像取数组元素一样操作指针。

        我们为什么说大部分情况可以等价操作,那就是有不等价的情况,两种情况,一是 指针是一个变量,可以像示例中 ptr = ptr+1; 即可以修改指针的值,而数组名不可以修改,它是常量;二是使用sizeof 进行计算,对数组应用 sizeof 运算符得到的是数组的长度,而对指针应用 sizeof 得到的是指针的长度,即使指针指向的是一个数组。

        还有一个平常容易忽略的点是数组的地址 ,对数组取地址时,数组名不会被解释为其地址。我们上边说了数组名被解释为数组的地址,相当于第一个数组元素的地址,当对数组名应用地址运算符时,得到的是整个数组的地址,从数字上说,这两个地址相同;但从概念上说,&ptr[0](即 ptr)是一个4 字节内存块的地址,而&ptr是一个20 字节内存块的地址。因此,表达式ptr+1将地址值加4,而表达式&ptr+1 将地址加 20。换句话说,ptr是一个int 指针,而&ptr是一个这样的指针,即指向包含5个元素的int数组(int(*)[5])。具体使用的时候一定要注意。

指针和字符串

        有三个特别需要注意的点

        1、在cout 和多数 C++表达式中,char 数组名、char 指针以及用引号括起的字符串常量都被解释为字符串第一个字符的地址。

        如果给 cout 提供一个字符的地址,则它将从该字符开始打印,直到遇到空字符为止。这意味着可以将指向char 的指针变量作为cout 的参数,因为它也是char 的地址。

cstring 中的strcpy 具体实现如图,可以看到从第一个字符的地址开始进行处理,直到碰到结束符为止

 

        2、在将字符串读入程序时,应使用已分配的内存地址。该地址可以是数组名,也可以是使用 new初始化过的指针。

        字符串读入一定是要有具体内存去存储它的,否则胡乱存储到未知内存的话,就会造成内存访问异常,进而出现程序挂死。而数组本身是一个已经由编译器分配好内存的的变量,new初始化过的指针也指向了一个动态分配好的内存。但是要特别注意,数组或者new 分配的内存要足够大,能够容纳需要读入的字符串,否则会造成内存访问溢出。

         

        3、字符串赋值给字符数组时要使用strcpy 或者 strncpy 等拷贝函数, 直接 "array = ptr" 这种操作是不可取的,因为ptr 是一个指针,其值是一个地址,直接赋值相当于 把ptr 指向的地址 赋值给array 数组了,但是array 数组本身是有一个地址的,编译器不允许修改 数组变量的地址

指针和结构体 

         可以运行时创建动态结构,“动态”意味着内存是在运行时,而不是编译时分配的。由于类与结构非常相似,结构的大部分技术也适用于类。将new 用于结构由两步组成:创建结构和访问其成员。要创建结构,需要同时使用结构类型和new。例如,要创建一个未命名的 inflatable 类型,并将其地址赋给一个指针,可以这样做:inflatable *ps = new inflatable; 这将把足以存储 inflatable 结构的一块可用内存的地址赋给 ps。

        动态创建的结构,不能将成员运算符句点用于结构名,因为这种结构没有名称,只是知道它的地址。需要使用箭头成员运算符(->) 来访问成员,这点和C 语言完全一眼。。例如,如果ps 指向一个inflatable 结构,则 ps->price 是被指向的结构的 price 成员。

        另一种访问结构成员的方法是,如果 ps 是指向结构的指针,则*ps 就是被指向的值--结构本身。由于*ps 是一个结构,因此(*ps).price 是该结构的 price 成员。但基本没这么用的,平白增加程序的复杂度。

局部变量、静态变量及动态变量        

        根据分配内存的方法,或者是变量所属内存位置的不同, C及 C++有 3 种管理数据内存的方式:自动存储、静态存储和动态存储(有时也叫作自由存储空间或堆), 对应的变量分别也叫局部变量、静态变量及动态变量。

      在函数内部定义的常规变量使用自动存储空间,被称为自动变量(automatic variable),这意味着它们在所属的函数被调用时自动产生,在该函数结束时消亡。自动变量是一个局部变量,其作用域为包含它的代码块。代码块是被包含在花括号中的一段代码。注意是代码块,而不是函数,我们目前为止举例都是整个函数举例,但其实一个函数里也可以分不同代码块的,如下所示, value 整形变量定义时使用{} 进行了包含, 所以属于一个单独的代码块,在代码快外使用,提示未定义。

         静态存储是整个程序执行期间都存在的存储方式。使变量成为静态的方式有两种: 一种是在函数外面定义它;另一种是在声明变量时使用关键字static, 如 static double fee=56.50;。

        自动存储和静态存储的关键在于,这些方法严格地限制了变量的寿命。变量可能存在于程序的整个生命周期(静态变量),也可能只是在特定函数被执行时存在(自动变量)。

        动态存储new 和delete 运算符提供了一种比自动变量和静态变量更灵活的方法。它们管理了一个内存池,这在 C++中被称为自由存储空间(free store)或堆(heap)。该内存池同用于静态变量和自动变量的内存是分开的。new 和delete 使得一个变量能够在一个函数中分配内存,而在另一个函数中释放它。因此,数据的生命周期不完全受程序或函数的生存时间控制。

        与使用常规变量相比,使用 new 和delete 让程序员对程序如何使用内存有更大的控制权。然而,内存管理也更复杂了。在栈中,自动添加和删除机制使得占用的内存总是连续的,但new和delete的相互影响可能导致占用的自由存储区不连续,这使得跟踪新分配内存的位置更困难。

        如果使用new 运算符在自由存储空间(或堆)上创建变量后,没有调用 delete,则即使包含指针的内存由于作用域规则和对象生命周期的原因而被释放,在自由存储空间上动态分配的变量或结构也将继续存在。实际上,将会无法访问自由存储空间中的结构,因为指向这些内存的指针无效。这将导致内存泄漏。被泄漏的内存将在程序的整个生命周期内都不可使用;这些内存被分配出去,但无法收回。

        我们在上一篇也提到过,指针时危险的,因为它们允许执行对计算机不友好的操作,如使用未经初始化的指针来访问内存或者试图释放同一个内存块两次,或者向上边提到的,因为未进行delete 操作,造成内存泄漏。大家使用的时候一定要特别注意。

数组的替代品

        模板类 vector 和array是数组的替代品

         模板类 vector 类似于string 类,是一种动态数组。可以在运行阶段设置vector 对象的长度,可在末尾附加新数据,还可在中间插入新数据。基本上,它是使用 new 创建动态数组的替代品。实际上,vector类确实使用 new 和 delete 来管理内存,但这种工作是自动完成的。

        要使用vector 对象,必须包含头文件vector。其次,vector 包含在名称空间std 中,因此可使用using 编译指令、using 声明或std::vector。第三,模板使用不同的语法来指出它存储的数据类型。第四,vector 类使用不同的语法来指定元素数。

        如下vi 是一个空的vector<int>对象 。 使用时可以使用对应的方法 resize 重新指定需要的大小并 按正常数组方式进行使用。

        vd 是一个有三个元素的 vector<double> 对象,定义时分配了空间,所以可以直接访问范围内元素。

        vector 类的功能比数组强大,但付出的代价是效率稍低。如果需要的是长度固定的数组,使用数组是更佳的选择,但代价是不那么方便和安全。鉴于此,C++11 新增了模板类array,它也位于名称空间std中。与数组一样,array 对象的长度也是固定的,也使用栈(静态内存分配),而不是自由存储区,因此其效率与数组相同。 好多学习资料上说array 有边界检查,不够准确,不是所有情况下都有边界检查,如下所示,使用max_size方法 ,获取到的确实 是定义时候的大小,但是 可以像C数组一样,超边界赋值,编译器并没有报错。只有通过at() 方法 时 才会捕捉这种越界错误,并进行停止,但是效率会 降低。

        

        到这里为止,符合类型就都总结完了,意味着数据类型也都学完了,下一篇我们将开始表达式和语句的总结学习。

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

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

相关文章

redis基本用法学习(C#调用FreeRedis操作redis)

FreeRedis属于常用的基于.net的redis客户端&#xff0c;EasyCaching中也提供适配FreeRedis的包。根据参考文献4中的说法&#xff0c;FreeRedis和CsRedis算是近亲&#xff08;都是GitHub中账号为2881099下的开源项目&#xff09;&#xff0c;因此其用法特别相似。FreeRedis的主要…

MoveIt!生成的机器人**_moveit_config包中config文件和launch文件

MoveIt!生成的机器人**_moveit_config包中config文件和launch文件 MoveItconfig文件srdfcartesian_limits.yamljoint_limits.yamlfake_controllers.yamsimple_moveit_controllers.yamlgazebo_controllers.yaml1. ros_controllers.yamlkinematics.yamlsensors_3d.yamlompl_plann…

【Qt之Quick模块】6. QML语法详解_1 基础语法与三种导入语句

前言 通过以上1-5文档的介绍&#xff0c;Quick与QML的概念及QML语法、类型、文件作用等已叙述个大概&#xff0c;接下来是对QML语法进行展开来说。 其实&#xff0c;学习任何一门语言或者做任何一件事情&#xff0c;并不用一开始就要求尽善尽美&#xff0c;做个无懈可击&…

vue3组件通信(父给子传参,子调用父的方法,父调用子的方法,顶层组件给底层组件传参,底层组件调用顶层组件的方法)

目录 1.父传子&#xff08;父给子传参&#xff09; 2.子传父&#xff08;子调用父的方法&#xff09; 3.父调用子的方法 4.顶层给底层传参&#xff0c;底层调用顶层的方法 5.模板引用 1.父传子&#xff08;父给子传参&#xff09; ①.步骤 父组件中给子组件通过绑定属性…

收银管理系统怎样帮助商家很好地经营服装门店

收银管理系统对于服装门店的经营可以提供多方面的帮助&#xff0c;以下是一些具体的优势和功能&#xff1a; 1. 快速准确的收银&#xff1a;收银管理系统可以实现快速、准确的收银操作&#xff0c;通过条码扫描或手动输入商品信息&#xff0c;自动计算价格并生成收据。这样可以…

nacos配置中心配置已经常见错误总结

&#x1f4bb;目录 前言1、基础架构2、依赖3、配置文件3.1、bolg-product配置文件3.1.1、application.yml配置文件3.1.2、bootstrap.yml配置文件3.1.3、nacos远程配置 3.2、bolg-system3.1.1、application.yml配置文件3.1.2、bootstrap.yml配置文件3.2.3、nacos远程配置 4、测试…

【文本处理】正则表达式

一、简介 正则表达式&#xff0c;又称规则表达式,&#xff08;Regular Expression&#xff0c;在代码中常简写为regex、regexp或RE&#xff09;&#xff0c;是一种文本模式&#xff0c;包括普通字符&#xff08;例如&#xff0c;a 到 z 之间的字母&#xff09;和特殊字符&…

在Redis客户端设置连接密码 并演示密码登录

我们先连接到Redis服务 然后 我们要输入 CONFIG SET requirepass “新密码” 例如 CONFIG SET requirepass "A15167"这样 密码就被设置成立 A15167 我们 输入 AUTH 密码 例如 AUTH A15167这里 返回OK说明成功了 然后 我们退出在登录就真的需要 redis-cli -h IP地…

嵌入式开发——PWM高级定时器

学习目标 加强掌握PWM开发流程理解定时器与通道的关系掌握多通道配置策略掌握互补PWM配置策略掌握定时器查询方式掌握代码抽取优化策略掌握PWM调试方式学习内容 需求 点亮8个灯,采用pwm的方式。 定时器 通道 <

vue3 新项目 - 搭建路由router

创建router/index 文件 main.ts 安装 router 然后 在 app下面 去 设置 路由出口

【贪心】买卖股票的最佳时机含手续费

/** 贪心&#xff1a;每次选取更低的价格买入&#xff0c;遇到高于买入的价格就出售(此时不一定是最大收益)。* 使用buy表示买入股票的价格和手续费的和。遍历数组&#xff0c;如果后面的股票价格加上手续费* 小于buy&#xff0c;说明有更低的买入价格更新buy。如…

深度神经网络下的风格迁移模型(C#)

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 这个是C#版本的&#xff0c;这里就只放出代码。VB.Net版本请参看 深度神经网络下的风格迁移模型-CSDN博客 斯坦福大学李飞飞团队的…

智能优化算法应用:基于天鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于天鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于天鹰算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.天鹰算法4.实验参数设定5.算法结果6.参考文献7.MA…

Unity PlayerPrefs存储数据在Windows环境中本地存储的位置

Unity PlayerPrefs存储数据在Windows环境中本地存储的位置 一、编辑器模式下的PlayerPrefs存储位置1.Win r 输入regedit进入注册表界面2. HKEY_CURRENT_USER/Software/Unity3.CompanyName和ProjectName可以在Unity->Edit->Project Settings->Player中查看和设置 二、…

Vue3数据交互axios

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 本…

Git系统有哪些优势

在现在的这个软件开发领域&#xff0c;版本控制是一项非常重要的工作。Git作为比较流行的分布式版本控制系统&#xff0c;他有着独特的优势成为了很多开发者们的首选。那Git系统都有哪些优势呢&#xff0c;下面我以自己的理解简单的介绍一下。 分布式版本控制的优势 Git用的是…

Java多线程、线程池及线程同步(synchronized关键字、悲观锁、乐观锁)

1.进程与线程定义 进程包含线程&#xff0c;如一个百度网盘进程&#xff0c;该进程的线程可以有上传&#xff0c;下载。 2.创建线程的三种方式 方式1-继承Thread类 方式2-实现Runnabled接口 1.常规写法 2.匿名内部类写法 方式3-实现Callable接口 示例代码&#xff1a; f1.get…

nginx 利用 error_page 实现自定义 404 跳转

文章目录 [toc]指定错误代码的 url 路径使用 response 来更改状态码使用 URL 重定向开始搞事情创建一个 404 文件配置 conf 文件通过 CURL 命令验证 error_page 以下内容&#xff0c;摘抄翻译自官网 语法格式 - error_page code ... [[response]] uri;上下文 - http, server, l…

【数据结构入门精讲 | 第十篇】考研408排序算法专项练习(二)

在上文中我们进行了排序算法的判断题、选择题的专项练习&#xff0c;在这一篇中我们将进行排序算法中编程题的练习。 目录 编程题R7-1 字符串的冒泡排序R7-1 抢红包R7-1 PAT排名汇总R7-2 统计工龄R7-1 插入排序还是堆排序R7-2 龙龙送外卖R7-3 家谱处理 编程题 R7-1 字符串的冒…

SpringSecurity6 | 失败后的跳转

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: Java从入门到精通 ✨特色专栏: MySQL学习 🥭本文内容: SpringSecurity6 | 失败后的跳转 📚个人知识库: Leo知识库,欢迎大家访问 学习…