目录
- 为什么需要动态内存分配?
malloc(Memory Allocate) - 分配内存- 函数原型
- 功能
- 使用示例
- 关键点与注意事项
realloc(Reallocate) - 重新分配内存- 函数原型
- 功能
- 使用示例
- 关键点与注意事项
malloc和realloc的核心区别- 完整的最佳实践示例
为什么需要动态内存分配?
在 C 语言中,内存主要分为几个区域:

(图片来源网络,侵删)
- 栈区:存储局部变量、函数参数等,编译器自动管理,速度快,但大小有限,当函数结束时,栈上的内存会自动释放。
- 静态/全局区:存储全局变量和静态变量,程序运行期间一直存在。
- 常量区:存储字符串字面量等常量。
- 堆区:这是动态内存分配的区域,程序员手动申请和释放,堆的大小通常比栈大得多,生命周期由程序员控制,直到
free()被调用。
什么时候需要堆内存?
- 编译时大小未知:你需要存储用户输入的字符串,但不知道用户会输入多长。
- 数据量非常大:如果数据量超过了栈的容量(通常是几 MB),就必须放在堆上。
- 需要长期存在:函数返回后,栈上的局部变量会销毁,如果希望数据在函数调用之间仍然有效,必须将其放在堆上。
malloc (Memory Allocate) - 分配内存
malloc 的作用是在堆区申请一块指定大小的连续内存空间。
函数原型
#include <stdlib.h> // 或 <malloc.h> void *malloc(size_t size);
- 参数:
size_t size- 你希望分配的内存字节数。 - 返回值:
- 成功时:返回一个指向分配好的内存块起始地址的
void*指针。 - 失败时:返回
NULL指针(因为堆内存可能已耗尽)。
- 成功时:返回一个指向分配好的内存块起始地址的
功能
- 向操作系统请求
size字节的内存。 - 如果请求成功,返回一个指向该内存的通用指针 (
void*)。 - 重要:
malloc不会初始化这块内存,内存中的值是随机的“垃圾值”。
使用示例
#include <stdio.h>
#include <stdlib.h>
int main() {
int n = 5;
int *ptr;
// 1. 分配足够的内存来存储 5 个 int
// sizeof(int) * 5 得到所需的总字节数
ptr = (int *)malloc(n * sizeof(int));
// 2. 检查分配是否成功
if (ptr == NULL) {
printf("内存分配失败!\n");
return 1; // 返回错误码
}
printf("成功分配了 %d 个 int 的内存,\n", n);
// 3. 使用这块内存 (像普通数组一样)
for (int i = 0; i < n; i++) {
ptr[i] = i * 10; // 初始化
printf("ptr[%d] = %d\n", i, ptr[i]);
}
// 4. 释放内存 (非常重要!)
free(ptr);
ptr = NULL; // 好习惯:将指针置为NULL,防止悬垂指针
return 0;
}
关键点与注意事项
- 包含头文件:必须包含
<stdlib.h>。 - 检查返回值:
malloc可能会失败,永远不要假设它一定成功,必须检查返回的指针是否为NULL。 - 类型转换:
malloc返回void*,通常需要将其转换为你需要的指针类型(如int*,char*),虽然现代 C 编译器(如 C99 之后)允许你直接赋值,但显式转换是更传统和清晰的做法。 - 计算大小:最好使用
sizeof(数据类型) * 数量的方式来计算所需字节数,而不是直接写一个数字,这样更具可移植性。 - 未初始化:
malloc分配的内存是“脏”的,必须在使用前手动初始化。 - 必须释放:使用完毕后,必须调用
free()将内存归还给系统,否则会导致内存泄漏。
realloc (Reallocate) - 重新分配内存
当你发现之前分配的内存空间不够用,或者想缩减它的大小时,就可以使用 realloc。
函数原型
#include <stdlib.h> void *realloc(void *ptr, size_t new_size);
- 参数:
void *ptr:指向之前由malloc,calloc, 或realloc分配的内存块的指针。size_t new_size:你希望调整到的新的大小(字节数)。
- 返回值:
- 成功时:返回一个指向新内存块的起始地址的指针。
- 失败时:返回
NULL指针,并且原来的内存块保持不变。
功能
realloc 是一个非常强大的函数,它处理以下几种情况:

(图片来源网络,侵删)
- 在原地扩展:
ptr指向的内存块后面有足够的空闲空间,realloc会直接在原地扩展这块内存,并返回原来的地址。 - 需要移动数据:如果后面的空间不够,
realloc会在堆上找一个足够大的新位置,分配一块new_size大小的内存,然后将旧内存块中的所有数据复制到新位置,然后释放旧内存块,并返回新地址。 - 缩小内存:
new_size可以比原来的小,realloc会缩小内存块,并返回原来的地址。 - 释放内存:
new_size为 0,realloc的行为等同于free(ptr),并返回NULL。 - 处理 NULL 指针:
ptr是NULL,realloc(new_size)的行为等同于malloc(new_size)。
使用示例
#include <stdio.h>
#include <stdlib.h>
int main() {
int *numbers = (int *)malloc(3 * sizeof(int));
if (numbers == NULL) {
printf("初始内存分配失败!\n");
return 1;
}
// 初始化并打印初始数据
for (int i = 0; i < 3; i++) {
numbers[i] = i + 1;
printf("numbers[%d] = %d\n", i, numbers[i]);
}
// 假设现在需要存储 5 个数字,而不是 3 个
printf("\n需要扩展内存...\n");
// 使用 realloc 扩展内存
int *temp = (int *)realloc(numbers, 5 * sizeof(int));
// 检查 realloc 是否成功
if (temp == NULL) {
printf("内存重新分配失败!\n");
free(numbers); // 即使失败,也要释放原来的内存
return 1;
}
// realloc 成功,它会返回一个新地址
// 必须更新你的指针变量,否则会丢失新内存的地址
numbers = temp;
// 新内存的旧部分数据是保留的,新部分是未初始化的
for (int i = 3; i < 5; i++) {
numbers[i] = i + 1; // 初始化新元素
}
printf("\n扩展后的数据:\n");
for (int i = 0; i < 5; i++) {
printf("numbers[%d] = %d\n", i, numbers[i]);
}
// 释放最终分配的内存
free(numbers);
numbers = NULL;
return 0;
}
关键点与注意事项
- 检查返回值:
realloc也可能失败。最经典的错误是直接ptr = realloc(ptr, new_size);。realloc失败并返回NULL,ptr就会被赋值为NULL,你就丢失了原来内存块的地址,导致无法释放,造成内存泄漏。 - 使用临时指针:正确的做法是先用一个临时指针接收
realloc的返回值,检查成功后再更新原指针。int *new_ptr = realloc(old_ptr, new_size); if (new_ptr != NULL) { old_ptr = new_ptr; } else { // 处理错误,但 old_ptr 仍然有效 printf("realloc 失败,但 old_ptr 仍然可用,\n"); } - 数据安全:
realloc保证旧数据会被完整地复制到新内存中(除非new_size比原来小)。 - 内存释放:
realloc成功,它会自动释放旧的内存块,你只需要在最后free()新的内存块即可。
malloc 和 realloc 的核心区别
| 特性 | malloc |
realloc |
|---|---|---|
| 目的 | 初次分配一块内存。 | 调整**已存在**内存块的大小。 |
| 参数 | 一个参数:要分配的字节数 (size)。 |
两个参数:旧内存地址 (ptr),新的大小 (new_size)。 |
| 对旧内存的处理 | 不涉及旧内存。 | 保留旧内存中的数据(除非缩小),如果移动,会自动释放旧内存。 |
| 对NULL指针的处理 | malloc(NULL) 是错误的用法。 |
realloc(NULL, size) 的行为等同于 malloc(size)。 |
完整的最佳实践示例
这个示例展示了如何创建一个可以动态增长的“数组”。
#include <stdio.h>
#include <stdlib.h>
int main() {
int capacity = 2; // 初始容量
int size = 0; // 当前元素个数
int *dynamic_array = (int *)malloc(capacity * sizeof(int));
if (dynamic_array == NULL) {
fprintf(stderr, "内存分配失败!\n");
return 1;
}
printf("请输入一系列整数,以 0 \n");
int input;
while (1) {
scanf("%d", &input);
if (input == 0) {
break; // 输入0结束
}
// 检查是否需要扩容
if (size == capacity) {
printf("数组已满,容量从 %d 扩展到 %d\n", capacity, capacity * 2);
capacity *= 2; // 容量翻倍
int *temp = (int *)realloc(dynamic_array, capacity * sizeof(int));
if (temp == NULL) {
fprintf(stderr, "内存重新分配失败! 已输入数据:\n");
// realloc失败,dynamic_array仍然指向原来的有效内存
break;
}
dynamic_array = temp; // 更新指针
}
dynamic_array[size] = input;
size++;
}
printf("\n您输入的数字是:\n");
for (int i = 0; i < size; i++) {
printf("%d ", dynamic_array[i]);
}
printf("\n");
// 释放内存
free(dynamic_array);
dynamic_array = NULL;
return 0;
}
malloc是你的“开荒工具”,用于在堆上申请一块全新的、未初始化的土地。realloc是你的“扩建工具”,用于调整已有土地的大小,它非常智能,可以原地扩建,也可以在别处找块新地并帮你搬家。- 黄金法则:
- 总是检查
malloc和realloc的返回值是否为NULL。 malloc/calloc分配的内存,最终必须用free释放。realloc可能返回新地址,务必用临时变量接收并检查后再更新你的指针。
- 总是检查
掌握了这两个函数,你就掌握了 C 语言动态内存分配的核心,能够编写出更灵活、更强大的程序。
