、
文章目录
- 一、为什么需要动态内存分配?
- [二、malloc 和 free](#二、malloc 和 free)
-
- [1. malloc](#1. malloc)
- [2. free](#2. free)
- [三、calloc 和 realloc](#三、calloc 和 realloc)
-
- [1. calloc](#1. calloc)
- [2. realloc](#2. realloc)
- 四、常见的动态内存错误
-
- [1. 对 NULL 解引用](#1. 对 NULL 解引用)
- [2. 越界访问](#2. 越界访问)
- [3. 对非动态内存使用 free](#3. 对非动态内存使用 free)
- [4. 释放部分动态内存](#4. 释放部分动态内存)
- [5. 多次释放同一块内存](#5. 多次释放同一块内存)
- [6. 内存泄漏](#6. 内存泄漏)
- 五、动态内存经典题目分析
- [六、柔性数组(Flexible Array)](#六、柔性数组(Flexible Array))
- [七、C/C++ 程序内存区域划分](#七、C/C++ 程序内存区域划分)
- 总结
一、为什么需要动态内存分配?
已掌握的内存开辟方式:
· int val = 20; → 栈上开辟4字节
· char arr[10] = {0}; → 栈上开辟10字节连续空间
局限性:
· 空间大小固定
· 数组长度必须在编译时确定
动态内存分配的优势:
· 程序运行时才能确定所需空间大小
· 动态申请和释放,更灵活
二、malloc 和 free
1. malloc
c
void* malloc(size_t size);
· 成功:返回指向开辟空间的指针
· 失败:返回 NULL
· 返回值类型为 void*,需强制转换
· 若 size = 0,行为未定义(编译器决定)
2. free
c
void free(void* ptr);
· 释放动态开辟的内存
· 若 ptr 非动态开辟,行为未定义
· 若 ptr 为 NULL,函数什么也不做
示例:
c
#include <stdio.h>
#include <stdlib.h>
int main() {
int num;
scanf("%d", &num);
int* ptr = (int*)malloc(num * sizeof(int));
if (ptr != NULL) {
for (int i = 0; i < num; i++) {
ptr[i] = 0;
}
}
free(ptr);
ptr = NULL; // 避免野指针
return 0;
}
三、calloc 和 realloc
1. calloc
c
void* calloc(size_t num, size_t size);
· 为 num 个大小为 size 的元素开辟空间
· 并将每个字节初始化为 0
· 与 malloc 的区别:自动初始化
示例:
c
int* p = (int*)calloc(10, sizeof(int));
// 输出:0 0 0 0 0 0 0 0 0 0
2. realloc
c
void* realloc(void* ptr, size_t size);
· 调整已开辟内存的大小
· ptr:原内存地址
· size:新大小
· 返回新内存起始地址
两种情况:
- 原空间后方有足够空间 → 直接扩展
- 原空间后方不足 → 另找空间,拷贝数据,释放原空间
使用建议:
c
int* tmp = (int*)realloc(ptr, new_size);
if (tmp != NULL) {
ptr = tmp;
} else {
// 处理失败
}
四、常见的动态内存错误
1. 对 NULL 解引用
c
int *p = (int*)malloc(INT_MAX/4);
*p = 20; // 可能崩溃
2. 越界访问
c
for (i = 0; i <= 10; i++) {
*(p + i) = i; // i=10 越界
}
3. 对非动态内存使用 free
c
int a = 10;
int *p = &a;
free(p); // 错误
4. 释放部分动态内存
c
p++;
free(p); // p 不指向起始位置
5. 多次释放同一块内存
c
free(p);
free(p); // 重复释放
6. 内存泄漏
c
void test() {
int *p = (int*)malloc(100);
// 忘记 free
}
五、动态内存经典题目分析
题目1:
c
void GetMemory(char *p) {
p = (char*)malloc(100);
}
// 错误:传值调用,str 仍为 NULL
题目2:
c
char* GetMemory() {
char p[] = "hello world";
return p; // 返回栈内存地址,危险!
}
题目3:
c
void GetMemory(char **p, int num) {
*p = (char*)malloc(num);
}
// 正确:传地址,可修改 str
题目4:
c
free(str);
if (str != NULL) { // str 已成为野指针
strcpy(str, "world"); // 非法访问
}
六、柔性数组(Flexible Array)
1.定义
c
struct st_type {
int i;
int a[]; // 或 int a[0];
};
2.特点
· 必须是结构最后一个成员
· sizeof 不包含柔性数组内存
· 需用 malloc 动态分配额外空间
使用:
c
type_a *p = malloc(sizeof(type_a) + 100 * sizeof(int));
p->i = 100;
for (int i = 0; i < 100; i++) {
p->a[i] = i;
}
free(p);
3.优势
· 一次分配、一次释放
· 内存连续,访问速度快,减少内存碎片
七、C/C++ 程序内存区域划分
区域 内容说明
栈区(stack) 局部变量、函数参数、返回地址等,系统自动分配释放
堆区(heap) 动态分配的内存,需手动管理
数据段(静态区) 全局变量、静态变量,程序结束释放
代码段 可执行代码、只读常量
内存映射段 文件映射、动态库、匿名映射
内核空间 用户代码不可访问
总结
· 动态内存管理使得程序在运行时能灵活申请和释放内存
· 使用 malloc、calloc、realloc 申请,free 释放
· 注意避免常见错误:空指针、越界、重复释放、内存泄漏等
· 柔性数组适用于动态大小的结构体成员
· 理解内存区域划分有助于更好地管理内存