870920 Menu

SwingCoder之C++备忘录·5

指针与动态内存分配

每个数据类型均可声明其指针变量(类型名之后,变量名之前使用“*”),指针变量存储的是某个数据的内存地址。指针变量前使用“*”为解引用,指针解引用表达式不仅可作为右值,还可作为左值(前提是,该指针非const指针)。指针之间可以相互赋值(前提依然是:右指针非const指针,或者两者都是const指针)。void*指针可以“指向”任何类型的数据。

指针分数据指针和函数指针两大类。函数名即该函数的函数指针,保存该函数的内存地址。可看出,C和C++中,变量、数组、函数、结构体、对象,都可以用指针的方式进行操作,也就是说,只要和内存数据有关的一切程序元素,均可基于其内存地址来操作。
int a = 10; // 声明一个int变量
int* pa = &a; // 声明指针变量,存储a的内存地址,可简称为pa“指向”a(的内存地址)
int b = *pa; // pa解引用,获取其指向的内存中所存储的数据,而后赋值给int变量b
*pa = 20; // pa解引用作为左值,将20赋值给其所“指向”的内存。此时,a == 20
// 指向变量的常指针。变量的值可以改变,指针不能再“指向”其它内存地址
int* const cp = &b;
// 指向常量的普通指针。指针可以“指向”其它内存地址,但无法修改所指常量的内存数据
const int* cip = a;
// 指向常量的常指针。指针不能“指向”其它内存地址,也无法修改该内存
const int* const cicp = a;

C语言分配和回收动态内存(堆内存),使用语言内置的函数malloc()与free(),关键点有三:

 malloc()返回值为void*类型(内存地址),如需赋值给该类型的指针,需使用强制类型转换

 malloc()的参数为需要分配的字节数,通常使用sizeof(类型)来计算所需的内存字节。分配数组堆内存时,可sizeof(类型) * 数组元素个数

 free()的参数为指针变量,相当于回收所分配的堆内存

示例(Complex为结构体类型):
/** 分配堆内存,地址赋值给指针变量。注意:分配后,并未在堆内存中创建Complex的对象,也就是说:内存中只是垃圾数据,malloc()并不会自动创建并初始化Complex的成员 */
Complex* pc = (Complex*) malloc (sizeof(Complex));
pc->real = 1.65; // 初始化成员…
free (pc); // 释放并回收堆内存

相比C语言,C++的动态内存分配与回收做了简化,使用运算符new和delete。使用new分配堆内存的同时即可创建堆对象(调用该类型的构造函数),而C语言的malloc()仅仅是分配堆内存,不会创建对象。同理,C++的delete不仅释放堆内存,而且还会调用析构函数销毁对象,而C语言的free()只是释放堆内存,却不能销毁对象(不调用类的析构函数)。因此,几乎所有的情况下,在C++中不应使用malloc()和free()。

// 下面这条语句有4个作用:分配堆内存,创建堆对象,声明指针,指针初始化为堆对象的内存地址
Complex* pc = new Complex(5.6, 6.2);
pc->doSomething();
delete pc; // C++中称为销毁堆对象,释放堆内存
pc = nullptr; // 保险起见,可在销毁堆内存后将指针置为nullptr
// 使用new和delete分配和释放数组所需的堆内存
// 为数组分配动态内存的前提是:数组元素的类型必须有默认的构造函数,否则,下句报错
const Complex* ptArray = new Complex [10];
for (int i = 10; –i >= 0; ) // 遍历数组
{
pc[i].display(); // 下标法
(*(pc + i)).display(); // 指针解引用法,必须使用两层小括号
}
// 销毁和释放堆内存的语法
delete [] pc;

注意:C++不能为多维数组一次性分配动态内存。多维数组如需使用动态分配的堆内存,每一维(即每一个内层数组)均需单独分配。