内存管理在 C/C++ 编程中尤为重要。尤其是 C 语言,缺少自动垃圾回收机制,程序员必须手动分配和释放内存。虽然这样提高了效率,但也容易出错。
本篇博客详细介绍 C 语言中的动态内存分配,包括基本函数的使用、常见错误,以及柔性数组的高级用法,帮助你写出更稳定、可靠的代码。
在编程中,我们经常会遇到这样的问题:
“我怎么知道一个数组应该多大?”
当我们写:
int arr[100];
这就是静态内存分配,它的大小在编译时就固定了。但在很多场景中,我们需要的大小是运行时才能确定的,这时候就需要动态内存分配。
#include
#include
int main() {
int n;
printf("请输入数组元素个数:");
scanf("%d", &n);
int* arr = (int*)malloc(n * sizeof(int));
if (arr == NULL) {
printf("内存申请失败\n");
return 1;
}
for (int i = 0; i < n; i++) {
arr[i] = i;
printf("%d ", arr[i]);
}
free(arr);
return 0;
}
malloc
是最常见的动态内存分配函数,它的作用是:
“在堆区分配一块指定大小的连续内存空间。”
语法如下:
void* malloc(size_t size);
size
是你想分配的字节数。void*
,需要强制类型转换。NULL
。int* p = (int*)malloc(5 * sizeof(int)); // 分配5个int的空间
if (p == NULL) {
printf("内存分配失败\n");
exit(1);
}
注意:malloc不会自动初始化,所以空间内是“垃圾值”。
分配了就必须释放。否则程序退出前这块内存一直被占着,造成内存泄漏。
free(p); // p 是malloc/calloc分配的指针
释放后建议把指针置为NULL,防止野指针:
free(p);
p = NULL;
int* p = (int*)malloc(3 * sizeof(int));
// ... 使用p
free(p);
p = NULL;
和 malloc
类似,但会把分配的内存全部初始化为0。
void* calloc(size_t n, size_t size);
n
是元素个数。size
是每个元素的大小。int* arr = (int*)calloc(5, sizeof(int));
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]); // 全是0
}
free(arr);
有时候分配的空间不够用,需要扩容,这时就用 realloc
。
void* realloc(void* ptr, size_t new_size);
它会尝试:
ptr
指向的内存扩容到 new_size
字节。int* p = (int*)malloc(3 * sizeof(int));
p[0] = 1; p[1] = 2; p[2] = 3;
int* temp = (int*)realloc(p, 5 * sizeof(int));
if (temp != NULL) {
p = temp;
p[3] = 4;
p[4] = 5;
}
for (int i = 0; i < 5; i++) {
printf("%d ", p[i]);
}
free(p);
注意: 原来的指针不能直接用 realloc
覆盖!避免扩容失败导致原内存丢失。
如果分配失败,返回 NULL
,解引用将会崩溃:
int* p = (int*)malloc(999999999 * sizeof(int));
*p = 1; // 崩溃
正确做法是:先判断是否为NULL。
int* p = (int*)malloc(3 * sizeof(int));
p[3] = 10; // 越界访问,未定义行为!
注意下标从 0 开始,最大只能到 2
。
int x = 10;
free(&x); // 错误!不是malloc分配的!
只能释放 malloc/calloc/realloc
分配的空间。
int* p = (int*)malloc(5 * sizeof(int));
free(p + 1); // 错误!只能释放p本身
必须释放完整起始地址。
int* p = (int*)malloc(4);
free(p);
free(p); // 错误,重复释放!容易崩溃
正确做法:
free(p);
p = NULL;
void func() {
int* p = (int*)malloc(100);
// 没有free(p),导致内存泄漏
}
程序运行时间长、开辟多了会让系统资源被吃光!
柔性数组是 C99 提出的结构体技巧,结构体最后一个元素声明为不定长数组:
struct S {
int n;
int arr[]; // 柔性数组
};
struct S {
int size;
int data[];
};
int main() {
int n = 5;
struct S* ps = (struct S*)malloc(sizeof(struct S) + n * sizeof(int));
ps->size = n;
for (int i = 0; i < n; i++) {
ps->data[i] = i * i;
}
for (int i = 0; i < n; i++) {
printf("%d ", ps->data[i]);
}
free(ps);
return 0;
}
内存区域 | 描述 |
---|---|
代码段 | 存放程序的机器指令 |
数据段 | 存储全局变量和静态变量的已初始化部分 |
BSS段 | 存储未初始化的全局变量/静态变量 |
栈区 | 存储函数调用时的局部变量、返回地址等,系统自动分配和释放 |
堆区 | 程序员手动管理的内存空间,需使用 malloc/free 手动操作 |
动态内存管理是 C 编程的必修课。你要学会使用:
malloc/free
分配和释放内存;calloc/realloc
进行初始化和扩容;一条原则:谁申请的,谁负责释放!