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

C++学习日志——面向过程篇3.11

1.内存的四个区域
C语言在程序执行时,其存储空间主要被分为四个区域:
1)代码区;
2)全局区:
3)堆区;
4)栈区;
而C++的存储区域划分和C语言是类似的,但是由于虚函数等作为C的扩展使得C++还要引入其他的区域来存储(如只读常量区、线程局部存储等),核心的存储分布是一样的。

2.代码区
代码区主要存储我们在ide中编辑的代码,并最终转化为01的二进制数由操作系统管理。代码区有两个特性,第一个是只读:

当程序运行后,这里的调控台也叫可执行文件,在该文件中我们无法对已生成的”Hello world“修改。
第二个特性是共享。对于一个未执行完成的程序,其生成的可执行文件(可以同时打开多个可执行文件)中的代码部分是相同的。

2.全局区
全局区主要存放全局变量、静态变量以及常量。我们先来看全局变量:

对于全局变量g_a和g_b,在main函数中输出其地址,观察发现其地址仅差4(十六进制8,9,A,B,C),这与g_a的数据类型int相符,这说明这两个全局变量的存放地址是连续的,这里我们在声明两个局部变量:

很明显局部变量的存储地址与全局变量存储位置相隔很远且不连续存放;而当我们把c更改为静态变量(添加关键字static后),程序运行结构如下:

此时c的地址和g_a与g_b连续且间隔为4,这说明c的存放空间由局部变量位置被修改至了与全局变量一致的全局区中,当我们把d也修改为静态变量时,发现它的存储空间于c连续,按照声明的先后顺序依次存放至全局区内。

对于常量而言,除了使用const修饰变量,还有一种更常见的字符串常量:

这里输出一个随便的字符串常量位置,发现它即使不和之前的全局变量、静态变量连续,但位置也十分相近,和局部变量时的存储空间差距很大但距离全局变量和静态变量的存储空间又十分相近,这说明字符串常量的存储空间也是位于全局区的。
对于局部常量,我们声明一个对应的局部常量e,并输出其地址:

发现和之前的存储空间相隔很远,把c和d修改为局部变量后再次输出:

发现局部常量e的存储空间是和局部变量之间的距离十分接近,这说明e并非存储于全局区。那全局常量又是如何呢?这里声明一个全局常量g_c观察输出结果:

发现其位置更接近存放于全局区的数据,说明全局常量的存放位置也在全局区。

3.栈区
栈作为一种数据结构,有着限制出入、先进后出的原则。实际上C++、C语言中的栈区也是遵循先进后出原则(LIFO)的,但是这里的堆和栈和并非数据结构中的堆和栈。
栈区和堆区的内存都是程序运行过程中申请和释放的,只是:栈区的内存是由操作系统来控制生命周期,而堆区的内存是根据使用者来控制生命周期的。
提起栈,我们联想到昨天学习过的野指针情况的最后一种情况:

这里也说明过,是把一个常量字符串赋值给一个在栈上的字符串变量,且返回的是栈上的局部变量首地址,在函数结束运行时s被销毁,于是char*(返回值)为野指针。那么这里我们不采用指针的返回而是使用C++的引用效果如何呢?

这里看似可以返回出一个正常值,但实际上也是错误的;因为这里代码希望返回的是字符数组的首地址,理论上输出应该是输出全部字符数组,但是这里的输出仅输出了字符数组的第一个元素;且在函数结束运行返回时s[20]就已经被销毁,实际上是没有s的地址的,就更没有对其取地址这个操作(引用传递的先导条件就是指向地址必须有实际意义)。
回到栈区的存储。栈区的存储内容主要有以下几种:
1)局部变量;
2)形式参数;
3)函数返回地址;
4)寄存器上下文(函数调用时);
除此以外,栈区的特点还有存储区域有限。

注意这里,a和b是函数Test的形式参数,而c和d是该函数内声明的局部变量,输出其内存地址发现地址位置类似,说明存储区域相近。

4.堆区
动态内存分配是C语言中很重要的内容,其中涉及了malloc,relloc以及free等函数的使用场景以及数据结构如链表、数组的空间分配等内容。而C++中使用更加便捷的new和delete来为这些数据结构进行动态内存管理;区别在于,C中的malloc等是函数,而new和delete是运算符。
实际上,动态分配内存就是使用者(程序员)自主的相系统申请数据的空间,而这些空间就是堆区的空间。

关于该程序,首先函数get的返回值是一个指向整型数据类型的指针。a是一个在函数内部申请的局部变量,因此a是存储于栈区的;此时通过运算符new为a分配一个整形类型的数据,并复制为v,该数据是存储于堆区的;当函数返回时,虽然栈区的a被释放掉了,但其指向内存仍然存在,因此只需要将该内存的地址赋值给指针p,所以main函数后续部分可以通过解引用输出p所指向内存的值。
除此以外,C++中使用new、delete运算符还可以实现调用类的构造函数来实例化对象:

最常见的就是初始化一个指向该类的对象的指针变量的初始化。
注意这里是如何使用new这个关键字的——new 数据类型(初值),如果这里不设置任何初值,理论上来讲该指针所指向的内容是一个随机值:

除了在声明中直接在数据类型(这里是int)添加括号,在括号内声明初值以外,也可以换行实现赋初值:

值得注意的是,这里不论是换行赋初值还是不赋初值ide都会默认报错,所以最好是在数据类型后直接声明其初值。
记着要随时释放指针并让指针置空:

关于声明一个指向整形元素数组的指针并使用new、delete运算符申请空间如下:

注意函数Getlist中的p存储于栈区,使用new运算符为其在堆区分配一个数组空间,并用指针p指向它,当函数执行完毕后p会自动释放,其地址不会被释放,而是赋值给了main中的p。此时的p也是一个指向元素为整形的数组的指针变量。在结束程序前,一定要对指针进行释放避免野指针,对于数组使用delete运算符的语句时,要在delete和指针名之间添加'[]'。

5.引用
C++中的引用就和C中的指针类似,除了在传参有区别,函数内部也可以代替指针操作:

通过修改b的值来实现对a的值的修改。这里&符号和指针中的*类似,在指针的声明中表示“这是一个指针类型的变量”,而在除声明外使用则是“对指针的解引用”;这里&在声明时表示“引用”,在其余位置表示“取地址”。注意使用引用修改值时的操作,并不像指针一样要“解引用”。

http://www.jsqmd.com/news/473738/

相关文章:

  • 架构2
  • ADRC双环自抗扰控制永磁同步电机矢量控制伺服系统Matlab仿真探索
  • IT系统全生命周期管理和运营方案(Word)
  • PYTHON学习笔记3
  • 代码随想录算法训练营第十天 | 用栈实现队列、 用队列实现栈、有效的括号、删除字符串中的所有相邻重复项
  • OFDM MQAM在衰落信道下误比特率性能仿真探索
  • python语法学习
  • Simulink双三相永磁同步电机控制仿真! 1.矢量控制,包括两种电机建模,VSD模型和双d...
  • STM32单片机开发的空气净化器:原理、设计与源码详解,适合开发人员
  • 探索多机器人协同编队避障算法:从人工势场到动态窗口
  • 从空白文档到合格初稿:Paperzz 毕业论文智能写作,让毕业生告别 “选题 - 文献 - 提纲” 三重焦虑
  • KPCA - ISSA - SVM分类预测:MATLAB实战与模型对比
  • Pyrene-PEG-NH2 氨基功能化芘荧光PEG活细胞成像与示踪探针
  • 产品推荐|40分区光控照明系统
  • 自动化测试中JSONPath 是解析复杂 JSON 响应的核心工具
  • binning模式下和normal模式下相同曝光参数相同场景加权亮度差异消除方案
  • LabVIEW图像处理框架核心结构示意图
  • 搞嵌入式开发的小伙伴应该都遇到过PID调参这个头疼的问题吧?今天咱们直接上干货,聊聊怎么在STM32上玩转PID自整定和温度控制。先扔个核心代码片段镇楼
  • HCSR04超声波测距仿真示例
  • 解决OpenWeatherMap API秘钥激活后仍无法使用
  • 基于STM32的电机控制器:Keil与Proteus的嵌入式之旅
  • 鸿蒙中 应用的权限(一)
  • 心理聊天App 5款产品实测对比,哪个更适合情绪内耗的你?
  • 内存涨价、供应不稳?嵌入式工程师必看:适合轻量级项目ARM选型与存储避坑指南
  • GESP C++一级认证完全指南:考点解析与备考策略
  • SpringBoot3实战集成mzt-biz-log,一行代码搞定业务日志记录
  • 电网电压扰动下相光伏并网逆变器控制的Simulink仿真探索
  • 技术人思维看渠道品牌管理:如何让“多渠道不走样”成为可执行的工程化规则?
  • 基于Matlab的螺丝轮廓识别:数字图像处理流程
  • STM32串口双机模拟汽车电量里程项目:Protues仿真与源码解析