梦想不会自己发光,真正闪耀的是那个为梦狂奔的你。献给知行的孩子们!(Eric.He著)
本教程将从 C++ 通用指针void*的特性、动态内存分配核心概念,到malloc、calloc、new三种动态内存分配方式的使用与区别,全面拆解动态内存管理的核心用法,帮助你掌握这一C++编程的关键技能。
void*(通用指针/无类型指针)是C/C++中的一种特殊指针类型,它可以指向任意数据类型的内存地址(int、char、结构体、类对象等),是实现通用内存操作的核心工具。
#include <iostream>
#include <cstring>
using namespace std;
int main() {
// 1. 基础类型指针转换为void*
int num = 100;
void* pVoid = # // 隐式转换,无需强制类型转换
// 2. void*转换回原类型指针(必须显式强转)
int* pInt = (int*)pVoid;
cout << "解引用void*转换后的指针:" << *pInt << endl; // 输出:100
// 3. void*指向不同类型数据
char str[] = "Hello void*";
pVoid = str;
char* pChar = (char*)pVoid;
cout << "void*指向字符串:" << pChar << endl; // 输出:Hello void*
// 4. 错误示例:void*无法直接解引用
// cout << *pVoid; // 编译报错:invalid use of void expression
// 5. 错误示例:void*无法直接做指针算术运算
// pVoid++; // 编译报错:arithmetic on a pointer to void
return 0;
}
动态内存分配是指程序运行时,从堆区(Heap)主动申请指定大小的内存空间,使用完毕后手动释放的内存管理方式(区别于栈区自动分配/释放的内存)。
| 内存区域 | 分配方式 | 释放方式 | 生命周期 | 空间大小 |
|---|---|---|---|---|
| 栈区(Stack) | 编译器自动分配 | 编译器自动释放 | 函数调用结束销毁 | 较小(MB级) |
| 堆区(Heap) | 手动调用函数/关键字分配 | 手动调用函数/关键字释放 | 直到手动释放或程序结束 | 较大(GB级) |
malloc是C语言标准库函数,用于从堆区分配指定字节数的内存空间,返回void*类型指针(需强制转换为目标类型),分配的内存未初始化(内容为随机值)。
#include // 必须包含头文件
void* malloc(size_t size); // size:需要分配的字节数
free()函数释放,否则内存泄漏;sizeof()计算(如malloc(sizeof(int)*5))。
#include <iostream>
#include <cstdlib> // malloc/free头文件
using namespace std;
int main() {
// 1. 分配能存储5个int类型的内存空间
int* pArr = (int*)malloc(sizeof(int) * 5);
// 2. 检查内存分配是否成功
if (pArr == NULL) {
cout << "内存分配失败!" << endl;
return -1;
}
// 3. 初始化并使用内存(malloc分配的内存未初始化)
for (int i = 0; i < 5; i++) {
pArr[i] = i + 1; // 手动赋值:1,2,3,4,5
cout << "pArr[" << i << "] = " << pArr[i] << endl;
}
// 4. 释放内存(必须手动释放)
free(pArr);
pArr = nullptr; // 避免野指针
return 0;
}
calloc是C语言标准库函数,与malloc功能类似,但会将分配的内存全部初始化为0,适合需要初始化为零的场景。
#include // 必须包含头文件
void* calloc(size_t num, size_t size);
// num:元素个数;size:每个元素的字节数
free()函数释放;
#include <iostream>
#include <cstdlib>
using namespace std;
int main() {
// 1. 分配5个int类型的内存空间,且自动初始化为0
int* pArr = (int*)calloc(5, sizeof(int));
// 2. 检查分配结果
if (pArr == NULL) {
cout << "内存分配失败!" << endl;
return -1;
}
// 3. 验证内存已初始化为0
cout << "calloc分配的内存初始值:" << endl;
for (int i = 0; i < 5; i++) {
cout << "pArr[" << i << "] = " << pArr[i] << endl; // 全部输出0
}
// 4. 赋值并使用
pArr[0] = 10;
pArr[1] = 20;
cout << "\n赋值后:" << endl;
cout << "pArr[0] = " << pArr[0] << ", pArr[1] = " << pArr[1] << endl;
// 5. 释放内存
free(pArr);
pArr = nullptr;
return 0;
}
new是C++关键字(非函数),专为C++设计的动态内存分配方式,支持类型安全、自动调用构造函数,是C++中推荐的动态内存分配方式。
// 1. 单个对象分配
类型* 指针变量 = new 类型; // 无参构造初始化
类型* 指针变量 = new 类型(初始化值); // 有参构造初始化
// 2. 数组分配
类型* 指针变量 = new 类型[元素个数];
// 3. 释放内存
delete 指针变量; // 释放单个对象
delete[] 指针变量; // 释放数组
bad_alloc异常(而非返回NULL);delete,数组用delete[](必须匹配)。
#include <iostream>
#include <new> // 异常处理头文件
using namespace std;
int main() {
try {
// 1. 单个int类型内存分配(可直接初始化)
int* pNum = new int(100);
cout << "new分配的int值:" << *pNum << endl; // 输出:100
// 2. 数组内存分配
int* pArr = new int[5]{1,2,3,4,5}; // C++11及以上支持直接初始化
cout << "\nnew分配的数组:" << endl;
for (int i = 0; i < 5; i++) {
cout << "pArr[" << i << "] = " << pArr[i] << endl;
}
// 3. 释放内存
delete pNum; // 释放单个对象
delete[] pArr; // 释放数组
pNum = nullptr;
pArr = nullptr;
} catch (bad_alloc& e) {
// 捕获内存分配失败异常
cout << "内存分配失败:" << e.what() << endl;
return -1;
}
return 0;
}
#include <iostream>
#include <cstring>
using namespace std;
// 定义结构体
struct Student {
char name[20];
int age;
// 构造函数
Student() {
strcpy(name, "未知");
age = 0;
cout << "无参构造函数调用" << endl;
}
Student(const char* n, int a) {
strcpy(name, n);
age = a;
cout << "有参构造函数调用" << endl;
}
// 析构函数
~Student() {
cout << "析构函数调用:释放" << name << "的内存" << endl;
}
};
int main() {
// 1. 单个结构体对象分配
Student* pStu = new Student("张三", 18);
cout << "学生姓名:" << pStu->name << ",年龄:" << pStu->age << endl;
// 2. 释放对象(自动调用析构函数)
delete pStu;
pStu = nullptr;
// 3. 结构体数组分配
Student* pStuArr = new Student[2]{Student("李四", 19), Student("王五", 20)};
delete[] pStuArr; // 释放数组,自动调用每个元素的析构函数
return 0;
}
| 特性 | malloc | calloc | new |
|---|---|---|---|
| 本质 | C库函数 | C库函数 | C++关键字 |
| 返回值 | void*(需强制类型转换) | void*(需强制类型转换) | 对应类型指针(无需转换) |
| 内存初始化 | 未初始化(随机值) | 自动初始化为0 | 可直接初始化(类/结构体调用构造函数) |
| 参数形式 | 单个参数(总字节数) | 两个参数(元素个数+单个元素字节数) | 类型+可选初始化值/数组长度 |
| 失败处理 | 返回NULL | 返回NULL | 抛出bad_alloc异常(默认) |
| 面向对象支持 | 不支持(仅分配内存,不调用构造/析构) | 不支持(仅分配内存,不调用构造/析构) | 支持(自动调用构造/析构函数) |
| 释放方式 | free() | free() | delete(单个对象)/delete[](数组) |
| 适用场景 | C/C++通用,无需初始化的内存分配 | C/C++通用,需要初始化为0的内存分配 | C++专属,尤其是类/结构体对象分配 |
本教程从通用指针void*到三种动态内存分配方式,全面拆解了C++动态内存管理的核心知识点。掌握动态内存分配与释放,是编写高效、稳定C++程序的必备技能,尤其在数据结构(链表、栈、队列等)实现中不可或缺。