【C/C++】 静态内存分配与动态内存分配
I - 内存分配概述
1.1 - 定义概述
内存分配 (Memory Allocation) 是指为计算机程序或服务分配物理内存空间或虚拟内存空间的一个过程。通常在程序执行前或执行时完成内存分配。
1.2 - 分类概述
存在两种类型的内存分配:
- 编译时内存分配或静态内存分配 (Compile-time or Static Memory Allocation)
- 运行时内存分配或动态内存分配 (Run-time or Dynamic Memory Allocation)
静态内存分配:
静态内存分配是由编译器为声明的变量分配内存。内存的地址可以通过地址操作符找到,并且可以赋值给指针变量。该内存是在编译时分配的。
动态内存分配 :
在程序执行时 (execution) 或 运行时 (run-time) 进行的内存分配被称为动态内存分配。库函数例如 calloc()
和 malloc()
或者操作符 new
均支持分配动态内存。动态分配的内存空间,通过这些函数或操作符的返回值分配,赋值给指针变量。
1.3 - 区别概述
序号 | 静态内存分配 | 动态内存分配 |
---|---|---|
1 | 在静态内存分配中,变量被永久地分配内存,直到程序执行结束/函数调用结束 | 在动态内存分配中,只有当你的程序单元被激活时才会为变量分配内存 |
2 | 静态内存分配在程序执行前完成 | 动态内存分配在程序执行过程中完成 |
3 | 使用栈来管理静态分配的内存 | 使用堆来管理动态分配的内存 |
4 | 较不高效 (less efficient) | 较高效 |
5 | 在静态内存分配中,不存在内存的重用 | 在动态内存分配中,存在内存的重用,而且在不需要时可以内存可以被释放 |
6 | 在静态内存分配中,一旦内存被分配,内存大小就不能再改变 | 在动态内存分配中,分配了内存后,内存的大小可以改变 |
7 | 在静态内存分配方案中,我们不能重新使用未使用的内存 | 动态内存分配中,允许重复使用内存。用户可以在需要时分配更多的内存。同时,用户也可以在需要时释放内存。 |
8 | 在这种内存分配方案中,执行速度比动态内存分配要快 | 在这种内存分配方案中,执行速度要比静态内存分配慢 |
9 | 编译时内存分配 | 运行时内存分配 |
10 | 静态分配的内存从程序开始保持到程序结束 | 动态分配的内存可以在任意时刻释放 |
11 | 静态的内存分配常常用于数组等 | 动态的内存分配常常用于链表等数据结构 |
II - 静态内存分配
内存必须被分配给我们所创建的变量,这样实际的变量才能存在。现在有一个问题,即我们认为它是如何运行的,以及它实际上是如何运行的?
计算机如何创建一个变量?
当我们思考如何创造某样东西时,我们会想到“从零开始”着手干,而当计算机创建一个变量 'X' 时,实际上情况并不是这样;对于计算机而言,更像是一种分配,计算机只是从许多预先存在的内存单元中分配一个内存单元给 X。
那么什么是静态内存分配?当我们声明变量时,我们实际上是在准备所有会被使用的变量,这样编译器就知道被使用的变量实际上是用户想要的程序的重要部分,而不是到处漂浮的流氓符号。因此,当我们声明变量时,编译器实际做的是将这些变量分配到它们的房间(一个内存单元)。现在,可以看出,这是在程序执行前完成的,你不能在程序执行时用这种方法分配变量。
示例
void func()
{
int a;
}
int main()
{
int b;
int c[12];
//...
}
上述代码中所有的变量都是静态分配的。
III - 动态内存分配
那么,既然已经存在一种方式来完成内存分配的工作,为什么我们需要引入另一种分配方法?为什么我们需要在程序执行过程中分配内存?
因为,尽管不是很显而易见,但不能在运行时分配内存,就降低了灵活性,并与空间效率相妥协。特别是,在那些事先不知道输入的情况下,我们会在存储的低效使用和缺乏或过多的空间用来输入数据方面受到影响(给定一个固定长度的数组或类似的数据结构来存储数据)。
所以引入动态内存分配: 在运行时,存储/内存/单元可以分配给变量的机制被称为动态内存分配(不要与 DMA 相混淆)。因此,我们可以知道在运行期间分配内存,这使我们能够使用我们想要的存储,而不用担心任何浪费或者不足。
为什么要使用动态分配的原因
- 当我们事先不知道程序需要多少内存的时;
- 当我们希望数据结构没有固定的内存空间上限时;
- 当你想更有效地使用你的内存空间时。例如: 如果你为一个一维数组分配的内存空间是
array[20]
,而你最终只使用了 10 个内存空间,那么剩下的 10 个内存空间就被浪费了,这些浪费的内存甚至不能被其他程序变量所使用; - 动态创建的列表的插入和删除可以非常容易地通过操作地址来完成,而在静态分配的内存中,插入和删除会导致更多的移动和内存浪费;
- 当你想在编程中使用结构和链表的概念时,动态内存分配是必须的
C++ 代码
int main(int argc, char* argv[])
{
// 动态内存分配
int* ptr = new int;
int* ptr1 = new int[10];
// 动态分配内存的释放
delete ptr;
delete [] ptr1;
}
C 代码
ptr = calloc(m, n);
等价于
ptr = malloc(m * n);
memset(ptr, 0, m * n);
IV - 小结
有两种类型的可用内存 -- 栈 (stack) 和堆 (heap)。静态内存分配只能在栈上进行,而动态内存分配可以在栈和堆上进行。在堆上进行动态分配的一个例子是递归,在递归中,函数按照出现的顺序被放入调用堆,并在到达基数时一个一个地弹出。
当在堆上分配内存时,我们需要手动删除内存,因为即使分配的内存范围结束(如栈的情况),内存也不会被编译器自己释放(取消分配 deallocate)。
4.1 - 静态分配的优缺点
优点
- 使用简单
- 分配和取消分配都由编译器完成
- 高效的执行时间
- 它使用栈数据结构
缺点
- 内存浪费问题
- 必须知道确切的内存需求
- 一旦初始化后,内存的大小不能调整
4.2 - 动态分配的优缺点
优点
- 动态分配是在运行时进行的
- 只要我们需要,我们就可以分配(创建)额外的存储
- 只要我们使用结束了,内存就可以被取消分配(free / delete)动态空间
- 因此,人们总是可以准确地拥有所需的空间量--不多也不少。
- 如果需要,内存大小可以重新分配
缺点
- 由于内存是在运行时分配的,因此需要更多的时间。
- 当完成后,内存需要由用户释放。这一点很重要,因为它更有可能变成难以发现的 bug。
总上所述,静态内存是编译器提前分配的东西。而动态内存是在执行过程中由程序控制的东西。程序可以要求更多的内存,也可以删除部分分配的内存。
`