void func(int x)
void increment(int x) { x++; } // 修改副本,不影响原始值
int a = 10;
increment(a); // a仍为10引用高效移动资源
void func(int* ptr)
*ptr
修改ptr != nullptr
void increment(int* ptr) {
if (ptr) (*ptr)++; // 安全检查
}
int a = 10;
increment(&a); // a变为11
void func(int& ref)
void increment(int& ref) { ref++; } // 直接操作原始值
int a = 10;
increment(a); // a变为11
特性 | 值传递 | 指针传递 | 引用传递 |
---|---|---|---|
操作对象 | 副本 | 原始数据(通过地址) | 原始数据(通过别名) |
是否修改原值 | 否 | 是 | 是 |
语法复杂度 | 简单(直接传值) | 较复杂(需解引用) | 简单(类似值传递) |
空值风险 | 无 | 有空指针风险 | 无(必须初始化) |
典型用途 | 简单数据、只读操作 | 需显式传递地址、可空 | 对象参数、避免拷贝 |
五、示例对比
// 值传递
void passByValue(int val) { val = 20; } // 不影响原始值
// 指针传递
void passByPointer(int* ptr) {
if (ptr) *ptr = 20; // 需检查空指针
}
// 引用传递
void passByReference(int& ref) { ref = 20; } // 直接修改
int main() {
int x = 10;
passByValue(x); // x仍为10
passByPointer(&x); // x变为20
passByReference(x); // x变为20
return 0;
}
void func(Type&& rvalue)
std::vector createVector() {
return std::vector{1,2,3};
}
std::vector vec = createVector(); // 通过右值引用高效移动资源
特性 | 数组指针(Pointer to Array) | 指针数组(Array of Pointers) |
---|---|---|
本质 | 指针:指向一个数组 | 数组:存储多个指针 |
语法 | int (*ptr)[5]; (括号强制 ptr 为指针) |
int* arr[5]; (arr 先与 [] 结合为数组) |
指向对象 | 整个数组 | 数组的元素(每个元素是一个指针) |
指针运算 | ptr + 1 跳过整个数组(如 5 个 int) |
arr + 1 指向下一个元素(下一个指针) |
典型用途 | 传递多维数组、精确控制内存布局 | 管理多个动态分配的对象、字符串数组 |
1. 数组指针(指向数组的指针)
int arr[5] = {1,2,3,4,5};
int (*ptr)[5] = &arr; // 指向包含5个int的数组
// 访问元素
(*ptr)[0] = 10; // 修改arr[0]为10
2. 指针数组(包含指针的数组)
int a = 1, b = 2, c = 3;
int* arr[3] = {&a, &b, &c}; // 数组的每个元素是int*
// 访问元素
*arr[0] = 10; // 修改a为10
1. 数组指针
ptr ──> [1, 2, 3, 4, 5] // 指向整个数组
ptr
存储整个数组的起始地址sizeof(ptr)
为指针大小(通常 4/8 字节)2. 指针数组
arr ──> [&a, &b, &c] // 数组元素为指针
│ │ │
▼ ▼ ▼
a b c
arr
是一个数组,包含多个指针sizeof(arr)
为 3 * sizeof(int*)
// 传递多维数组
void printMatrix(int (*matrix)[4], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
}
int main() {
int matrix[3][4] = {...};
printMatrix(matrix, 3); // matrix退化为int (*)[4]
}
2. 指针数组的应用
// 字符串数组
const char* fruits[3] = {
"Apple",
"Banana",
"Cherry"
};
// 动态内存管理
int* ptrs[5];
for (int i = 0; i < 5; i++) {
ptrs[i] = new int(i);
}
int (*ptr)[5]; // 数组指针:ptr是指向包含5个int的数组的指针
int* ptr[5]; // 指针数组:ptr是包含5个int*的数组
int arr[5];
int* ptr1 = arr; // 指向首元素的指针(隐式转换)
int (*ptr2)[5] = &arr; // 指向整个数组的指针
printf("%p\n", arr); // 数组首元素地址
printf("%p\n", &arr); // 整个数组的地址(数值相同,但类型不同)
printf("%p\n", arr + 1); // 跳过1个元素
printf("%p\n", &arr + 1); // 跳过整个数组(5个元素)
特性 | 指针函数(Function Returning Pointer) | 函数指针(Pointer to Function) |
---|---|---|
本质 | 函数:返回值为指针类型 | 指针:指向一个函数 |
语法 | int* func(int a); (返回 int*) |
int (*ptr)(int a); (ptr 为指针) |
用途 | 返回动态分配的内存或全局变量地址 | 作为参数传递函数、实现回调机制 |
调用方式 | int* result = func(10); |
int val = (*ptr)(10); 或 ptr(10); |
int* createArray(int size) {
int* arr = new int[size];
for (int i = 0; i < size; i++) {
arr[i] = i;
}
return arr; // 返回动态分配的数组指针
}
// 调用
int* ptr = createArray(5);
int add(int a, int b) { return a + b; }
// 定义函数指针并初始化
int (*op)(int, int) = add;
// 调用方式1
int result = (*op)(3, 4); // 显式解引用
// 调用方式2(C++允许隐式解引用)
int result2 = op(3, 4); // 等价于上一行
// 返回静态变量的地址
const char* getMessage() {
static const char* msg = "Hello";
return msg;
}
2. 函数指针的应用
// 回调函数示例
void process(int a, int b, int (*func)(int, int)) {
int result = func(a, b);
printf("Result: %d\n", result);
}
int main() {
int (*add)(int, int) = [](int a, int b) { return a + b; };
process(3, 4, add); // 输出7
}
int* func(int a); // 指针函数:返回int*
int (*ptr)(int a); // 函数指针:ptr指向返回int的函数
2. 函数指针作为参数
// 排序函数接受比较函数指针
void sort(int* arr, int size, bool (*compare)(int, int)) {
// 排序逻辑...
}
bool ascending(int a, int b) { return a < b; }
// 调用
sort(array, 10, ascending);
特性 | malloc |
calloc |
---|---|---|
初始化 | 不初始化分配的内存(内容随机) | 将内存初始化为 0 |
参数 | 单个参数:所需内存字节数 | 两个参数:元素数量和元素大小 |
原型 | void* malloc(size_t size); |
void* calloc(size_t num, size_t size); |
性能 | 略快(无需初始化) | 略慢(需清零内存) |
int* ptr = (int*)malloc(5 * sizeof(int)); // 分配5个int的内存
if (ptr != NULL) {
// 内存内容未初始化,可能包含随机值
for (int i = 0; i < 5; i++) {
printf("%d ", ptr[i]); // 输出随机值
}
}
int* ptr = (int*)calloc(5, sizeof(int)); // 分配5个int的内存并初始化为0
if (ptr != NULL) {
// 内存内容已初始化为0
for (int i = 0; i < 5; i++) {
printf("%d ", ptr[i]); // 输出: 0 0 0 0 0
}
}
三、内存布局差异
// malloc分配的内存(未初始化)
ptr ──> [随机值][随机值][随机值][随机值][随机值]
// calloc分配的内存(初始化为0)
ptr ──> [0][0][0][0][0]
安全性:
calloc
适合需要初始化的场景(如存储结构体、数组)malloc
需手动初始化(如使用memset
):int* ptr = malloc(5 * sizeof(int));
memset(ptr, 0, 5 * sizeof(int)); // 手动清零
性能:calloc
因初始化操作会稍慢
场景 | 推荐函数 | 原因 |
---|---|---|
存储需要初始化的数据 | calloc |
自动清零,避免未定义行为 |
存储无需初始化的数据 | malloc |
略高效 |
分配二进制缓冲区 | malloc |
后续会写入数据,无需提前初始化 |
分配结构体数组 | calloc |
确保结构体成员初始化为有效值 |
malloc
/new
)未被正确释放(如free
/delete
),导致这部分内存永久无法被回收忘记释放内存:
void func() {
int* ptr = new int[100]; // 分配内存
// 忘记调用delete[] ptr;
}
异常导致路径未释放:
void func() {
int* ptr = new int[100];
if (condition) {
throw std::exception(); // 异常退出,未释放内存
}
delete[] ptr;
}
指针覆盖:
int* ptr = new int;
ptr = new int; // 原内存丢失,无法释放
循环分配内存:
while (true) {
int* ptr = new int[1000]; // 持续分配,无释放
}
类中未定义析构函数:
class Resource {
public:
Resource() { data = new int[100]; }
// 未定义析构函数释放data
private:
int* data;
};
cppcheck --enable=all --inconclusive your_file.cpp
Valgrind(Linux):
valgrind --leak-check=full ./your_program
cpp
运行
#include
void func() {
std::unique_ptr ptr(new int[100]); // 自动释放
// 无需手动delete
}
cpp
运行
#include
void func() {
std::vector data(100); // 自动管理内存
}
try-catch
确保资源释放: cpp
运行
void func() {
int* ptr = new int[100];
try {
// 可能抛出异常的代码
} catch (...) {
delete[] ptr;
throw;
}
delete[] ptr;
}
malloc
→ free
new
→ delete
new[]
→ delete[]
类型 | 用途 |
---|---|
std::unique_ptr |
独占所有权 |
std::shared_ptr |
共享所有权(引用计数) |
std::weak_ptr |
弱引用,避免循环引用 |