动态内存
很好地理解动态内存到底如何在 C 中发挥作用是成为一个好的 C 程序员所必需的。 C 程序中的内存分为两个部分:
- 栈:所有函数内部声明的变量会占用栈的内存。
- 堆:这是程序中未使用的内存,可以在程序运行时动态地分配内存。
很多时候,你事先不知道你在一个定义的变量中需要多少内存来存储特定的信息以及在程序运行时所需内存的大小。
你可以在运行时为指定类型的变量分配堆内存,并且可以使用 C 中特殊操作符返回分配空间的地址。这个操作符被称为 new
操作符。
如果你不再需要动态分配内存了,你可以使用 delete
操作符来释放之前用 new
操作符分配的内存。
new 和 delete 操作符
下面是使用 new
操作符为任意数据类型动态地分配内存的通用的语法。
new data-type;
这里,data-type
可以是任何内置数据类型,包括数组或任何用户定义的数据类型包括类或结构。让我们先看看内置的数据类型。例如,我们可以定义一个 double 类型的指针然后在程序执行时请求分配内存。我们可以使用new
操作符来完成它,程序语句如下:
double* pvalue = NULL; // Pointer initialized with null
pvalue = new double; // Request memory for the variable
如果自由存储区都已经被占用,内存可能就不能被成功分配。因此检查new
操作符是否返回空指针是一种很好的做法,并且要采取适当的措施如下:
double* pvalue = NULL;
if(!(pvalue = new double)) {
cout << "Error: out of memory." <<endl;
exit(1);
}
C 语言中的 malloc()
函数在 C 中仍然存在,但是建议避免使用 malloc() 函数。相对于 malloc() 函数 new 操作符的主要优势是 new 操作符不仅分配内存,它还可以构造对象,而这正是 C 的主要目的。
在任何时候,当你觉得一个变量已经不再需要动态分配,你可以用 delete 操作符来释放它在自由存储区所占用的内存,如下:
代码语言:javascript复制delete pvalue;// Release memory pointed to by pvalue
让我们把理解一下这些概念,并且用下面的例子来说明 new 和 delete 是如何起作用的:
代码语言:javascript复制#include <iostream>
using namespace std;
int main() {
double* pvalue = NULL; // Pointer initialized with null
pvalue = new double; // Request memory for the variable
*pvalue = 29494.99; // Store value at allocated address
cout << "Value of pvalue : " << *pvalue << endl;
delete pvalue; // free up the memory.
return 0;
}
如果我们编译和运行上面的代码,这将产生以下结果:
代码语言:javascript复制Value of pvalue : 29495
数组的动态内存分配
考虑到你想要为字符数组分配内存,即20个字符的字符串。使用与上面相同的语法我们可以动态地分配内存,如下所示。
代码语言:javascript复制char* pvalue = NULL; // Pointer initialized with null
pvalue = new char[20]; // Request memory for the variable
应该像这样删除我们刚刚创建的数组声明:
代码语言:javascript复制delete [] pvalue;// Delete array pointed to by pvalue
学习过 new 操作符的类似通用语法,你可以为一个多维数组分配内存如下:
代码语言:javascript复制double** pvalue = NULL; // Pointer initialized with null
pvalue = new double [3][4]; // Allocate memory for a 3x4 array
然而,释放多维数组内存的语法仍然同上:
代码语言:javascript复制delete [] pvalue;// Delete array pointed to by pvalue
对象的动态内存分配
对象与简单的数据类型并无不同。例如,考虑下面的代码,我们将使用一个对象数组来解释这个概念:
代码语言:javascript复制#include <iostream>
using namespace std;
class Box {
public:
Box() {
cout << "Constructor called!" <<endl;
}
~Box() {
cout << "Destructor called!" <<endl;
}
};
int main() {
Box* myBoxArray = new Box[4];
delete [] myBoxArray; // Delete array
return 0;
}
如果你为四个 Box 对象数组分配内存,一个简单的构造函数将被调用四次,同样的删除这些对象时,析构函数也被调用相同的次数。
如果我们编译和运行上面的代码,这将产生以下结果:
代码语言:javascript复制Constructor called!
Constructor called!
Constructor called!
Constructor called!
Destructor called!
Destructor called!
Destructor called!
Destructor called!