diff --git a/01-C语言基础与进阶/Readme.md b/01-C语言基础与进阶/Readme.md index a581c84..9e126f8 100644 --- a/01-C语言基础与进阶/Readme.md +++ b/01-C语言基础与进阶/Readme.md @@ -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 ``` ### ✅ 函数指针 / 函数指针数组