核心概念:为什么需要动态内存分配?
在 C 语言中,内存分为几个区域:

(图片来源网络,侵删)
- 栈区:存储局部变量、函数参数等,由编译器自动管理,速度快,但大小有限,函数返回时,其栈上的变量会被销毁。
- 静态/全局区:存储全局变量和
static变量,程序启动时分配,结束时释放。 - 常量区:存储字符串字面量等常量。
- 堆区:这是动态内存分配的区域,由程序员手动管理,堆的大小通常比栈大得多,生命周期由程序员控制,直到被
free释放。
什么时候需要动态内存?
- 当你需要的内存大小在编译时无法确定(从用户输入获取数组大小)。
- 当你需要的数据量非常大,超过了栈的容量限制。
- 当你需要的数据需要在函数之间共享,而函数返回后栈上的数据会失效。
malloc (Memory Allocate)
malloc 的作用是在堆上分配一块指定大小的连续内存空间。
函数原型
#include <stdlib.h> // 必须包含这个头文件 void *malloc(size_t size);
参数
size_t size:你想要分配的内存字节数。
返回值
- 成功时:返回一个指向分配好的内存块起始地址的
void*指针。 - 失败时:如果堆上没有足够的连续内存空间,
malloc会返回NULL指针。
关键特点
- 内存是未初始化的:
malloc分配的内存中是“垃圾数据”,你必须在使用前手动进行初始化(用memset或循环赋值)。 - *返回 `voidmalloc` 返回的是一个通用指针,你需要根据你的数据类型进行强制类型转换。
- 只分配,不初始化:它只负责在堆上找一块空间给你,这块空间里是什么内容它不关心。
使用示例
#include <stdio.h>
#include <stdlib.h>
int main() {
int n;
printf("请输入要存储的整数个数: ");
scanf("%d", &n);
// 1. 检查输入是否合法
if (n <= 0) {
printf("输入无效,\n");
return 1;
}
// 2. 使用 malloc 分配内存
// sizeof(int) * n 计算需要的总字节数
// (int*) 将 void* 强制转换为 int*,因为我们要存储整数
int *ptr = (int*)malloc(n * sizeof(int));
// 3. **至关重要的一步:检查分配是否成功**
if (ptr == NULL) {
printf("内存分配失败!\n");
return 1; // 分配失败,程序退出
}
// 4. 使用分配的内存(从键盘读入数据)
printf("请输入 %d 个整数:\n", n);
for (int i = 0; i < n; i++) {
scanf("%d", &ptr[i]);
}
// 5. 打印内存中的数据
printf("你输入的整数是:\n");
for (int i = 0; i < n; i++) {
printf("%d ", ptr[i]);
}
printf("\n");
// 6. **使用完毕后,必须释放内存!**
free(ptr);
// 7. 将指针置为 NULL,这是一个好习惯,防止“野指针”
ptr = NULL;
return 0;
}
realloc (Re Allocate)
realloc 的作用是重新调整一块已经分配好的内存的大小,你可以用它来扩大或缩小内存块。
函数原型
#include <stdlib.h> void *realloc(void *ptr, size_t new_size);
参数
void *ptr:指向之前由malloc,calloc, 或realloc分配的内存块的指针,如果你想分配一个全新的内存块,可以传入NULL,realloc的行为和malloc一样。size_t new_size:你希望调整到的新的总字节数。
返回值
- 成功时:返回一个指向调整后的内存块起始地址的
void*指针。 - 失败时:如果无法满足
new_size的要求,返回NULL。
realloc 的工作原理(非常重要!)
realloc 的行为比 malloc 复杂,因为它取决于是否有足够的连续空间:

(图片来源网络,侵删)
-
如果原地址后面有足够的连续空间
realloc会在原地扩展内存块,并将原有数据复制到新扩展的部分。- 返回的指针和原来的
ptr相同。
-
如果原地址后面没有足够的连续空间
realloc会在堆的其他地方寻找一块大小为new_size的新内存。- 找到后,它会将原来内存块中的所有数据复制到新内存中。
- 它会释放掉原来的内存块。
- 返回的是新内存块的地址,这个地址和原来的
ptr不同。
-
new_size比原来小realloc会截断内存块,只保留前面的new_size字节。- 返回的指针和原来的
ptr通常相同。
使用示例
#include <stdio.h>
#include <stdlib.h>
int main() {
int initial_size = 5;
int *numbers = (int*)malloc(initial_size * sizeof(int));
if (numbers == NULL) {
printf("初始内存分配失败!\n");
return 1;
}
// 初始化并打印初始数组
printf("初始数组 (%d 个元素):\n", initial_size);
for (int i = 0; i < initial_size; i++) {
numbers[i] = i * 10;
printf("%d ", numbers[i]);
}
printf("\n\n");
// 假设我们需要更多的空间
int new_size = 10;
int *temp_ptr = (int*)realloc(numbers, new_size * sizeof(int));
// **检查 realloc 是否成功**
if (temp_ptr == NULL) {
printf("内存重新分配失败!\n");
// 原有的 numbers 指针仍然有效,不能 free 它
// 我们只能继续使用原来的大小
printf("继续使用原来的数组,\n");
} else {
// realloc 成功,更新指针
// 注意:temp_ptr 可能是新的地址,也可能是旧的地址
numbers = temp_ptr;
printf("数组已成功扩展到 %d 个元素:\n", new_size);
for (int i = 0; i < new_size; i++) {
printf("%d ", numbers[i]); // 原有数据仍然存在,新位置是未初始化的垃圾数据
}
printf("\n");
}
// ... 使用扩展后的数组 ...
// 释放内存
free(numbers);
numbers = NULL;
return 0;
}
malloc vs realloc 对比总结
| 特性 | malloc |
realloc |
|---|---|---|
| 功能 | 分配一块全新的内存 | 调整已存在的内存块大小 |
| 参数 | size_t size (所需字节数) |
void *ptr (原指针), size_t new_size (新字节数) |
| 初始数据 | 内存是未初始化的 | 保留原有数据,新扩展部分是未初始化的 |
| 失败处理 | 返回 NULL,原指针无效 |
返回 NULL,原指针仍然有效,数据未丢失 |
| 典型用法 | int *p = malloc(n * sizeof(int)); |
p = realloc(p, new_n * sizeof(int)); |
最佳实践和常见错误
- 总是检查返回值:
malloc和realloc都可能失败,必须检查它们返回的指针是否为NULL。 realloc失败时不要丢弃原指针:如上所述,realloc失败时返回NULL,但你的旧内存块和其中的数据都还在,如果你直接free了旧指针,就会造成内存泄漏。// 错误示范 ptr = realloc(ptr, new_size); if (ptr == NULL) { free(ptr); // 错误!ptr 是 NULL,但原来的内存块已经找不到了 }// 正确示范 int *new_ptr = realloc(ptr, new_size); if (new_ptr == NULL) { // 分配失败,但 ptr 仍然指向有效的旧内存 printf("分配失败,但旧数据还在,\n"); // 可以选择继续使用 ptr } else { // 分配成功,更新指针 ptr = new_ptr; }free之后将指针置为NULL:这可以防止“悬垂指针”(Dangling Pointer),即一个指向已释放内存的指针,通过检查if (ptr != NULL)可以避免对已释放内存的误操作。- 只
free你malloc/calloc/realloc过的指针:不要释放未分配的内存或栈上的变量地址。 - 不要对
NULL指针调用realloc:虽然realloc的第一个参数允许是NULL(此时行为等同于malloc),但如果你已经将一个指针free并置为NULL,再对它调用realloc是不安全的,最好用malloc来处理全新内存的分配。 - 计算字节时使用
sizeof:始终使用sizeof(type)来计算单个元素的大小,而不是硬编码数字(如4代表int的大小),这样代码更具可移植性。
掌握了 malloc 和 realloc,你就能编写出更灵活、更强大的 C 语言程序,能够处理动态变化的数据结构,如动态数组、链表等。

(图片来源网络,侵删)
