Update Readme.md
This commit is contained in:
+171
-19
@@ -49,31 +49,183 @@ const int MAX_SIZE = 100;
|
||||
---
|
||||
|
||||
### ✅ 栈 和 堆(内存管理)
|
||||
- **栈 (stack)**:自动分配内存,函数退出即释放。
|
||||
- 示例:局部变量、函数参数等
|
||||
- **堆 (heap)**:使用 `malloc` / `free` 手动分配和释放
|
||||
#### 栈 (stack):自动分配内存,函数退出即释放。
|
||||
1. 核心特性
|
||||
- 自动分配与释放:由编译器自动管理,函数调用时分配栈帧,函数返回时自动释放。
|
||||
- 后进先出(LIFO):类似一摞盘子,最后放入的最先取出。
|
||||
- 高速访问:栈内存访问效率高(通常通过寄存器直接操作)。
|
||||
- 空间有限:栈空间通常较小(如 Linux 默认 8MB),过大的局部变量可能导致栈溢出。
|
||||
|
||||
2. 存储内容
|
||||
- 局部变量:函数内部定义的变量。
|
||||
- 返回地址:函数执行完毕后返回的位置。
|
||||
- 函数参数:调用函数时传递的参数。
|
||||
- 寄存器值:保存调用前的寄存器状态,以便恢复。
|
||||
|
||||
3. 工作原理
|
||||
- 栈指针(ESP):指向当前栈顶的内存地址。
|
||||
- 栈帧(Stack Frame):每个函数调用在栈上分配的独立空间,包含局部变量和参数。
|
||||
- 示例代码:
|
||||
```c
|
||||
// 栈内存示例
|
||||
void func() {
|
||||
int a = 10; // a 在栈中
|
||||
}
|
||||
// 堆内存示例
|
||||
int *p = (int *)malloc(sizeof(int) * 10);
|
||||
if (p) {
|
||||
p[0] = 100;
|
||||
free(p);
|
||||
void func(int a, int b) {
|
||||
int sum = a + b; // sum存储在栈上
|
||||
// ...
|
||||
} // 函数返回时,sum和参数a、b自动释放
|
||||
```
|
||||
4. 优缺点
|
||||
- 优点:无需手动管理内存,速度快,不会内存泄漏。
|
||||
- 缺点:生命周期固定(函数结束即释放),空间有限。
|
||||
|
||||
#### 堆 (heap):使用 `malloc` / `free` 手动分配和释放
|
||||
1. 核心特性
|
||||
|
||||
- 手动分配与释放:使用malloc/calloc/realloc分配,free释放。
|
||||
- 动态生命周期:内存块的生命周期由程序员控制,可跨函数使用。
|
||||
- 碎片化问题:频繁分配和释放可能导致内存碎片,降低空间利用率。
|
||||
- 慢速访问:需通过指针间接访问,效率低于栈。
|
||||
|
||||
2. 存储内容
|
||||
- 动态分配的对象:如malloc返回的内存块。
|
||||
- 大型数据结构:如数组、链表、树等需要动态调整大小的结构。
|
||||
- 跨函数数据:需要在函数调用结束后继续存在的数据。
|
||||
|
||||
3. 工作原理
|
||||
- 内存管理器:操作系统提供的堆管理器负责分配和回收内存。
|
||||
- 空闲链表:堆管理器维护空闲内存块列表,分配时查找合适大小的块。
|
||||
- 示例代码:
|
||||
```c
|
||||
void createArray() {
|
||||
int* arr = (int*)malloc(10 * sizeof(int)); // 从堆分配内存
|
||||
if (arr != NULL) {
|
||||
arr[0] = 100; // 使用堆内存
|
||||
// ...
|
||||
}
|
||||
free(arr); // 手动释放内存
|
||||
}
|
||||
```
|
||||
4. 常见函数
|
||||
- malloc(size_t size):分配指定字节的内存,不初始化。
|
||||
- calloc(size_t num, size_t size):分配内存并初始化为 0。
|
||||
- realloc(void* ptr, size_t new_size):调整已分配内存的大小。
|
||||
- free(void* ptr):释放内存,必须与malloc配对使用。
|
||||
|
||||
**栈 vs 堆的对比**
|
||||
| 特性 | 栈(Stack) | 堆(Heap) |
|
||||
|--------------|--------------------------------------|---------------------------------------------|
|
||||
| 分配方式 | 自动(由编译器管理) | 手动(如 `malloc`/`free` 或 `new`/`delete`)|
|
||||
| 生命周期 | 函数调用期间自动创建和销毁 | 程序员控制,需手动释放 |
|
||||
| 内存空间 | 连续、有限(通常几 MB) | 不连续、较大(受限于物理内存) |
|
||||
| 访问速度 | 快(通过寄存器快速访问) | 慢(通过指针间接访问) |
|
||||
| 内存碎片 | 不存在(先进后出结构) | 可能产生(频繁分配和释放) |
|
||||
| 使用场景 | 局部变量、函数调用栈帧 | 动态数据结构、跨函数共享数据 |
|
||||
|
||||
### ✅ 指针 / 指针数组 / 二级指针
|
||||
- **指针**:保存变量的地址
|
||||
- **指针数组**:数组中每个元素是一个指针
|
||||
- **二级指针**:指向指针的指针
|
||||
#### 指针:保存变量的地址
|
||||
1. 核心概念
|
||||
|
||||
- 指针是变量:存储的是内存地址,而非实际数据。
|
||||
- 类型关联:指针类型必须与所指向变量的类型匹配(如int*指向int)。
|
||||
- 间接访问:通过解引用操作符*访问或修改指向的内存。
|
||||
2. 声明与初始化
|
||||
```c
|
||||
int val = 42;
|
||||
int *p = &val;
|
||||
int **pp = &p;
|
||||
printf("%d\n", **pp); // 输出 42
|
||||
int num = 10; // 普通变量
|
||||
int* ptr = # // 指针ptr指向num的地址
|
||||
|
||||
// 等价于两步:
|
||||
int* ptr; // 声明指针
|
||||
ptr = # // 初始化指针
|
||||
```
|
||||
3. 解引用操作
|
||||
```c
|
||||
printf("%d", *ptr); // 输出:10(访问指针指向的值)
|
||||
*ptr = 20; // 修改指针指向的内存,num变为20
|
||||
```
|
||||
4. 指针运算
|
||||
```c
|
||||
int arr[3] = {1, 2, 3};
|
||||
int* p = arr; // p指向arr[0]
|
||||
printf("%d", *p); // 输出:1
|
||||
p++; // 指针后移,指向arr[1]
|
||||
printf("%d", *p); // 输出:2
|
||||
```
|
||||
|
||||
#### 指针数组:数组中每个元素是一个指针
|
||||
1. 核心概念
|
||||
|
||||
- 数组本质:连续存储多个指针变量。
|
||||
- 用途:常用于处理多个字符串或动态分配的内存块。
|
||||
2. 声明与初始化
|
||||
```c
|
||||
// 存储3个int指针的数组
|
||||
int a = 10, b = 20, c = 30;
|
||||
int* ptrArray[3] = {&a, &b, &c};
|
||||
|
||||
// 存储字符串常量的指针数组(每个元素是char*)
|
||||
char* fruits[3] = {
|
||||
"Apple",
|
||||
"Banana",
|
||||
"Cherry"
|
||||
};
|
||||
```
|
||||
3. 访问与应用
|
||||
```c
|
||||
// 访问指针数组中的值
|
||||
printf("%d", *ptrArray[0]); // 输出:10(a的值)
|
||||
|
||||
// 遍历字符串数组
|
||||
for (int i = 0; i < 3; i++) {
|
||||
printf("%s\n", fruits[i]); // 输出每个字符串
|
||||
}
|
||||
```
|
||||
4. 典型场景
|
||||
- 字符串处理:避免字符串拷贝,直接操作地址。
|
||||
- 动态内存管理:存储多个动态分配的内存块(如malloc返回的指针)。
|
||||
|
||||
#### 二级指针:指向指针的指针
|
||||
1. 核心概念
|
||||
|
||||
- 双重间接性:二级指针存储的是一级指针的地址。
|
||||
- 用途:常用于函数参数传递指针的地址,以便修改原始指针。
|
||||
2. 声明与初始化
|
||||
```c
|
||||
int num = 100;
|
||||
int* ptr = # // 一级指针指向num
|
||||
int** ptr2 = &ptr; // 二级指针指向ptr
|
||||
|
||||
// 等价于:
|
||||
int** ptr2;
|
||||
ptr2 = &ptr;
|
||||
```
|
||||
3. 解引用操作
|
||||
```c
|
||||
printf("%d", **ptr2); // 输出:100(两次解引用)
|
||||
**ptr2 = 200; // 修改num的值为200
|
||||
```
|
||||
4. 典型应用场景
|
||||
|
||||
(1)函数中修改指针
|
||||
```c
|
||||
void allocateMemory(int** p) {
|
||||
*p = (int*)malloc(sizeof(int)); // 修改传入的指针
|
||||
**p = 100; // 修改指针指向的值
|
||||
}
|
||||
|
||||
// 调用
|
||||
int* ptr = NULL;
|
||||
allocateMemory(&ptr); // 传递指针的地址
|
||||
printf("%d", *ptr); // 输出:100
|
||||
```
|
||||
|
||||
(2)二维数组动态分配
|
||||
```c
|
||||
// 创建3x3的二维数组
|
||||
int** matrix = (int**)malloc(3 * sizeof(int*));
|
||||
for (int i = 0; i < 3; i++) {
|
||||
matrix[i] = (int*)malloc(3 * sizeof(int));
|
||||
}
|
||||
|
||||
// 访问元素
|
||||
matrix[0][1] = 5; // 等价于*(*(matrix+0)+1) = 5
|
||||
```
|
||||
|
||||
### ✅ 函数指针 / 函数指针数组
|
||||
|
||||
Reference in New Issue
Block a user