笔记复习
1.C++的内存分区模型
a.定义
在讲解内存分区模型之前,我们需要明白什么是内存
内存(Memory),也称为 RAM(Random Access Memory),是计算机中的一种临时存储设备,用于在程序运行时存储数据。当程序运行时,它会将各种数据(如变量、对象、函数调用信息等)存储在内存中,以便快速访问和处理。
明白了内存的概念之后,我们来了解一下内存分区,内存分区指的是将内存分为多个区域,在C++将内存分为:栈区,堆区,全局区(也叫静态区),常量区,代码区五个区域,其中常量区是用来存放常量数据的,这些数据在程序运行期间不能被修改,这个区域的内存通常在编译时确定,在运行时十分安全,因此下面我们不讲解常量区
b.深入分析
到这里,我们对内存分区模型的定义已经有了一个基本的认知,下面我们借助三个常见的问题更进一步理解内存分区模型
问题1:为什么C++要使用内存分区模型,将内存分为多个区域?
想象一下,如果你把所有的东西都放在一个大箱子里,想要找某样东西的时候会非常麻烦,对吧?同样地,如果所有的数据都放在同一块内存区域,那么管理这些数据、访问它们或释放不再需要的数据就会变得非常困难。因此,为了更好地管理和使用内存资源,C++采用了内存分区模型。
问题2:C++为什么要分配和回收内存?
1.动态需求:
程序在运行过程中可能会遇到一些事先无法确定的需求,例如用户输入的数据大小、动态创建的对象等。这时就需要在运行时动态地分配内存。
动态分配内存使得程序更加灵活,能够根据实际需要分配合适的内存空间。
2.节省资源:
如果所有内存都在编译时静态分配,会导致资源浪费。例如,一个程序可能只需要在某些特定情况下使用大量内存,而在其他时候几乎不需要额外的内存。动态分配内存可以有效利用资源,避免浪费。
回收内存
1.避免内存泄漏:
当程序不再需要某块内存时,如果不及时释放,这块内存就会一直被占用,导致内存泄漏。
内存泄漏会逐渐消耗系统可用的内存资源,最终可能导致程序崩溃或系统性能下降。
2.优化性能:
及时回收不再使用的内存,可以让系统有更多的可用内存来处理其他任务,从而提高整体性能。
问题3:我们常见的代码错误内存泄露是什么意思?
我们先来阐明内存泄露的定义:
内存泄漏(Memory Leak)是指程序在申请内存后,由于某种原因未能正确释放,导致这部分内存无法再次被使用。随着时间的推移,内存泄漏会逐渐累积,最终可能导致程序或系统耗尽可用内存,引发各种问题,如程序变慢、崩溃或系统不稳定。
内存泄露是程序在申请内存后由于某种原因未能正确释放导致的,因此我们首先要明白内存释放是什么,内存释放实际上就是回收内存,将计算机分配给我的内存还回去,相当于告诉计算机,这个内存我不用了,你拿走吧,这样就不会出现内存泄露的问题。
c.内存四区简述
由于内存四区过于复杂,想要完全掌握可能需要多年的编程经验,因此我们这里只简单讲解内存四区各自的功能以及如何在编译器中判断一个变量是属于哪一个区域的,重点讲解部分是我们经常会出错的点,例如内存泄露、重复释放等。
1.栈区(Stack)
栈区用于存储函数的局部变量(在函数体中定义的变量)和函数调用的信息(主要是参数列表中给的参数,其他的很少见)。当一个函数被调用时,它所需的所有局部变量都会被分配到栈上,当函数执行完毕后,这些变量所占用的空间会被自动释放。
2.堆区(Heap)
堆区用于动态分配内存。程序员可以使用 new 关键字在堆上分配内存,并使用 delete 来释放不再需要的内存。
堆区提供了更大的灵活性,但是管理起来也更加复杂,容易出现内存泄漏的问题,即忘记释放已经分配的内存。
代码示例:
int* pInt = new int; // 分配一个int类型的内存
*pInt = 10; // 初始化
delete pInt; // 释放内存
当我们使用new关键字分配内存后,这个变量就会被存储在堆区中。
3.全局/静态区(Global/Static)
这个区域用于存储全局变量和静态变量。这些变量的生命周期贯穿整个程序的运行期,直到程序结束才会被释放。
全局/静态区中的数据在程序启动时被初始化,在程序退出时被清理。
a.全局变量:不在函数体中定义的变量
b.静态变量:静态变量有三种,分别是静态全局变量,静态局部变量以及类的静态成员变量,在变量定义时的数据类型前加上ststic关键字即可得到静态变量(静态变量过于复杂,因此这里不详细介绍)
4.代码区(Code/Text)
代码区存储了程序的机器指令,这部分内存是只读的(对于程序而言,也就是说不存在编写一段代码实现把这段代码删除的功能的操作),以防止程序意外修改自己的指令。
每个程序都有一个唯一的代码区,它包含了程序执行的所有指令。
注:代码区和全局区是程序运行前就有的区域,栈区和堆区是程序运行后才有的区域
d.关于内存的常见问题
1.内存泄露
原因:分配内存后没有释放内存
#include <iostream>
#include <cstdlib>
void leakMemory() {
int*ptr1 = new int[1000];
int*ptr2 =new int[10];
// 动态分配内存
// 忘记释放内存
}
int main() {
while (true) {
leakMemory();
}
return 0;
}
解决方法:
手动释放内存:当我们分配一个int型变量时需要手动释放单个变量:
delete ptr1
当我们分配一个int型数组时需要手动释放数组:
delete[] ptr2
2.内存溢出
原因:程序尝试分配超过可用内存的内存空间,导致程序崩溃或异常
#include <iostream>
int main() {
int* largeArray = new int[1000000000]; // 尝试分配过大的内存
delete[] largeArray;
return 0;
}
解决办法:不要分配那么大的内存或者使用动态数组,根据需要来决定是否要分配内存
#include <iostream>
#include <vector>
int main() {
std::vector<int> data; // 使用动态数组
for (int i = 0; i < 1000000000; ++i) {
data.push_back(i); // 动态添加数据
}
return 0;
}
3.栈溢出
常见的原因是因为使用递归算法时深度过大
#include <iostream>
void recursiveFunction(int depth) {
if (depth > 0) {
recursiveFunction(depth - 1); // 递归调用
}
}
int main() {
recursiveFunction(1000000); // 调用深度过大
return 0;
}
解决办法:不要递归那么深