本文目录
使用new分配内存
在C++中,为一个数据对象(可以是结构,也可以是基本类型)获得并制定分配内存的通用格式:
typeName * pointer_name = new typeName;
例如:在运行阶段为一个int值法分配未命名的内存:
int * pn = new int;
内存区域
对于指针,需要说明:new分配的内存块通常与常规变量声明分配的内存块不同。变量和指针的值都存储在栈(stack)的内存区域中,而new从堆(heap)或自由存储区(free store)的内存区域分配内存。
对于上述说明的补充:
栈(Stack)和堆(Heap):
- 栈:用于存储局部变量和函数调用信息,由系统自动管理内存分配和释放。栈内存的大小是有限的,且速度较快。
- 堆:由程序显式管理内存,使用如new和delete进行分配和释放。堆内存较大且灵活,但分配速度比栈慢。
变量的存储:
- 常规变量声明:如int x = 10;分配在栈上,编译器会在变量生命周期结束时自动释放内存。
- 指针变量:指针本身也是一种变量,它存储的是地址。这个地址指向其他内存位置,可以是栈上,也可以是堆上的数据。
new分配内存:
当使用new分配内存时,分配的是堆上的内存,指针本身依然存在于栈上。程序员需要显式使用delete释放这块内存,否则会造成内存泄漏。
使用new创建动态数组
在C++中,创建动态数组很容易;只要将数组的元素类型和元素数目告诉new即可。必须在类型名后加上方括号,其中包含元素数目。
type_name * pointer_name = new type_name [num_elements];
例如,要创建一个包含10个int元素的数组,可以这样做:
int * psome = new int [10];
当程序使用完new分配的内存块时,应使用delete释放他们。
delete [] psome;
使用delete释放内存
delete运算符的功能是将使用的内存归还给内存池。使用delete时,后面要加上指向内存块的指针(最初是用new分配的)
例如:
int * ps = new int;
. . .
delete ps;
上述操作会释放ps指向的内存,但不会删除指针ps本身。例如,可以将ps重新指向另一个新分配的内存块。
注意:
一定要配对地使用new和delete;否则将发生内存泄漏(memory leak),也就是说被分配的内存再也无法使用了。
不要尝试释放已经释放的内存块,C++标准指出,这样做的结果将是不确定的,这意味着什么情况都可能发生。另外,不能使用delete来释放声明变量所获得的内存。
int * ps = new int; //OK
delete ps; //ok
delete os; //not ok now
只能用delete来释放使用new分配的内存。然而,对空指针使用delete是安全的。
不要创建两个指向同一个内存块的指针,因为这将增加错误地删除同一个内存块两次的可能性
总结:
使用new和delete时,应遵守以下规则:
- 不要使用 delete 来释放不是 new 分配的内存。
- 不要使用 delete 释放同一个内存块两次。
- 如果使用 new[] 为数组分配内存,则应使用 delete[] 来释放。
- 如果使用 new[] 为一个实体分配内存,则应使用delete(没有方括号)来释放。
- 对空指针应用 delete 是安全的。
sizeof运算符 与 指针
对数组应用sizeof运算符得到的是数组的长度,而对指针应用sizeof得到的是指针的长度,即使指针指向的是一个数组。
Q1:下述代码应该输出什么?
#include <iostream>
int main() {
int tacos[10];
int* pz = new int[10];
std::cout << "sizeof(tacos)=" << sizeof(tacos) << std::endl;
std::cout << "sizeof(pz)=" << sizeof(pz) << std::endl;
return 0;
}
A1:
sizeof(tacos)=40
sizeof(pz)=8
分析:
tacos是一个包含10个int元素的静态数组。在sizeof运算中,静态数组的名称并不会退化为指针,而是代表整个数组。因此,sizeof(tacos)的值是数组占用的总字节数,即sizeof(int) * 10
pz是一个指针变量(类型为int*
),它存储的是动态分配数组的地址。对指针变量使用sizeof时,返回的是指针本身的大小,与指针指向的数据类型或分配内存的大小无关。指针的大小取决于平台(32位系统通常为4字节,64位系统通常为8字节)。
补充:栈、堆和内存泄漏
(C++ premier plus 4.8.5 自动存储、静态存储和动态存储)
如果使用 new 运算符在自由存储空间(或堆)上创建变量后,没有调用 delete,将发生什么情况呢?如果没有调用 delete,则即使包含指针的内存由于作用域规则和对象生命周期的原因而被释放,在自由存储空间上动态分配的变量或结构也将继续存在。实际上,将会无法访问自由存储空间中的结构,因为指向这些内存的指针无效。这将导致内存泄漏。被泄漏的内存将在程序的整个生命周期内都不可使用:这些内存被分配出去,但无法收回。极端情况(不过不常见)是,内存泄漏可能会非常严重,以致于应用程序可用的内存被耗尽,出现内存耗尽错误,导致程序崩溃。另外,这种泄漏还会给一些操作系统或在相同的内存空间中运行的应用程序带来负面影响,导致它们崩溃。即使是最好的程序员和软件公司,也可能导致内存泄漏。要避免内存泄漏,最好是养成这样一种习惯,即同时使用 new 和 delete 运算符,在自由存储空间上动态分配内存,随后便释放它。
注意:指针是功能最强大的 C++工具之一,但也最危险,因为它们允许执行对计算机不友好的操作,如使用未经初始化的指针来访问内存或者试图释放同一个内存块两次。另外,在通过实践习惯指针表示法和指针概念之前,指针是容易引起迷惑的。由于指针是 C++编程的重要组成部分,本书后面将更详细地讨论它。本书多次对指针进行了讨论,就是希望您能够越来越熟悉它。