一、指针与函数参数:传值、传地址、传引用的区别
1. 普通传值
void foo(int x) {
x = 10;
}
- 拷贝变量副本,不影响原值
2. 传指针(地址)
void foo(int* px) {
*px = 10;
}
int a = 5;
foo(&a); // 传地址
- 可以修改原变量的值
3. 传引用(C++ 语法糖)
void foo(int& x) {
x = 10;
}
- 效果和传指针一样,语法更简洁
- 本质上编译器仍然使用地址传递
二、堆与栈的内存分配(Stack vs Heap)
特性 | 栈内存(stack) | 堆内存(heap) |
---|---|---|
分配方式 | 编译器自动分配 | 程序员手动申请 (new , malloc ) |
生命周期 | 函数结束自动释放 | 手动释放 (delete , free ) |
速度 | 快 | 相对较慢 |
空间大小 | 小 | 大 |
示例:栈 vs 堆
void stack_example() {
int a = 5; // 分配在栈上
int b[1000]; // 数组也在栈上,超大会爆栈
}
void heap_example() {
int* pa = new int(5); // 在堆上
int* arr = new int[1000]; // 大数组建议放堆上
delete pa;
delete[] arr;
}
三、数组与指针的底层实现原理
1. 数组名是常量指针
int arr[5] = {1,2,3,4,5};
int* p = arr; // 等价于 &arr[0]
cout << arr[2] << " == " << *(arr + 2) << endl;
2. 指针模拟数组
int* p = new int[5];
for (int i = 0; i < 5; ++i)
p[i] = i * 10;
底层数组的访问是通过偏移地址寻址:
*(p + i) ↔ p[i]
3. 指针和二维数组
int arr[2][3]; // 实际是一维连续内存
int* p = &arr[0][0]; // 可以线性访问
四、内存申请与释放(new/delete 和 malloc/free)
1. C 风格:malloc / free
int* p = (int*)malloc(sizeof(int) * 10);
p[0] = 1;
free(p);
malloc
不会调用构造函数,适用于 C 或 POD 类型。
2. C++ 风格:new / delete
int* p = new int(5);
int* arr = new int[10];
delete p;
delete[] arr;
new
自动计算大小,并调用构造函数。
3. 自定义结构体对象
struct Point {
int x, y;
Point(int a, int b): x(a), y(b) {}
};
Point* pt = new Point(3, 4);
delete pt;
五、常见错误和防坑技巧
错误类型 | 描述 |
---|---|
内存泄漏 | new 后没有 delete ,会占用系统内存不释放 |
野指针 | 使用未初始化或已释放的指针 |
重复释放 | 两次 delete 同一指针(可用 nullptr 判定) |
越界访问 | 指针运算出错,访问了非法内存 |
安全写法建议
int* p = new int(10);
delete p;
p = nullptr; // 防止二次 delete
六、综合例子(封装动态数组类)
#include <iostream>
using namespace std;
class MyArray {
int* data;
int size;
public:
MyArray(int n): size(n) {
data = new int[n];
}
~MyArray() {
delete[] data;
}
int& operator[](int index) {
return data[index];
}
};
int main() {
MyArray arr(5);
for (int i = 0; i < 5; ++i)
arr[i] = i * 10;
for (int i = 0; i < 5; ++i)
cout << arr[i] << " ";
cout << endl;
}
七、总结结构图
栈(Stack):
┌─────────────┐
│ int a = 5; │ ← 自动分配、函数返回即释放
└─────────────┘
堆(Heap):
┌─────────────┐
│ new int(5); │ ← 手动管理,delete 释放
└─────────────┘
数组与指针:
arr[i] ↔ *(arr + i)