为什么需要动态内存分配?
在 C 语言中,变量的内存分配方式主要有两种:

(图片来源网络,侵删)
- 静态内存分配:在编译时确定内存大小,如
int a[100];,大小固定,不能在程序运行时改变。 - 动态内存分配:在程序运行时,根据需要向操作系统申请内存,这种方式非常灵活,可以处理数据大小不确定的情况(读取用户输入的未知数量的数据)。
malloc 和 calloc 就是实现动态内存分配的两个标准库函数。
malloc (Memory Allocation)
malloc 的作用是在堆上分配一块指定大小的连续内存空间。
函数原型
#include <stdlib.h> // 或 <malloc.h> void *malloc(size_t size);
参数
size_t size: 你想要分配的内存字节数。
返回值
- 成功: 返回一个指向分配内存块起始地址的
void*指针。void*是一个通用指针,你可以将它转换为任何类型的指针。 - 失败: 返回
NULL指针,如果内存不足或请求为 0,malloc可能会失败,此时必须检查返回值。
特点
- 不初始化:
malloc分配的内存是未初始化的,里面是“垃圾值”(Garbage Value),你必须手动将值写入这块内存。 - 只分配指定大小的内存:它只关心你请求的字节数,不关心你用来接收它的指针类型。
使用示例
#include <stdio.h>
#include <stdlib.h>
int main() {
int n = 5;
int *ptr;
// 1. 分配内存
// sizeof(int) * n 计算总共需要多少字节
ptr = (int *)malloc(n * sizeof(int)); // 将 void* 转换为 int*
// 2. 检查分配是否成功
if (ptr == NULL) {
printf("内存分配失败!\n");
return 1; // 退出程序
}
printf("malloc 成功分配了 %d 个 int 的内存,\n", n);
// 3. 使用内存(必须手动初始化)
for (int i = 0; i < n; i++) {
ptr[i] = i + 10; // 给每个元素赋值
}
// 4. 打印内存内容
printf("ptr 指向的数组内容: ");
for (int i = 0; i < n; i++) {
printf("%d ", ptr[i]);
}
printf("\n");
// 5. 释放内存
free(ptr); // 释放后,ptr 变成“野指针”,最好立即置为 NULL
ptr = NULL;
return 0;
}
calloc (Contiguous Allocation)
calloc 的作用是在堆上分配一块内存,并将其所有位初始化为 0。
函数原型
#include <stdlib.h> // 或 <malloc.h> void *calloc(size_t num, size_t size);
参数
size_t num: 要分配的元素个数。size_t size: 每个元素的大小(字节数)。
返回值
- 成功: 返回一个指向分配内存块起始地址的
void*指针。 - 失败: 返回
NULL指针。
特点
- 自动初始化为 0:
calloc分配的内存会被清零,这对于数组结构特别有用,可以避免使用未初始化的数据。 - 参数是元素个数和大小:它的设计更侧重于分配数组。
使用示例
#include <stdio.h>
#include <stdlib.h>
int main() {
int num_elements = 5;
int *ptr;
// 1. 分配内存
// 分配 5 个 int 大小的内存,并全部初始化为 0
ptr = (int *)calloc(num_elements, sizeof(int));
// 2. 检查分配是否成功
if (ptr == NULL) {
printf("内存分配失败!\n");
return 1;
}
printf("calloc 成功分配了 %d 个 int 的内存,并初始化为 0,\n", num_elements);
// 3. 打印内存内容(可以直接看到是 0)
printf("ptr 指向的数组内容: ");
for (int i = 0; i < num_elements; i++) {
printf("%d ", ptr[i]);
}
printf("\n");
// 4. 使用内存(可以直接赋值,因为已经是 0)
ptr[0] = 100;
ptr[4] = 200;
printf("修改后的数组内容: ");
for (int i = 0; i < num_elements; i++) {
printf("%d ", ptr[i]);
}
printf("\n");
// 5. 释放内存
free(ptr);
ptr = NULL;
return 0;
}
malloc vs calloc 核心区别
| 特性 | malloc |
calloc |
|---|---|---|
| 功能 | 分配指定字节的内存 | 分配指定数量和大小的内存,并清零 |
| 参数 | 1 个:总字节数 (size_t size) |
2 个:元素个数 (size_t num) 和 元素大小 (size_t size) |
| 内存初始化 | 不初始化是垃圾值 | 自动初始化为 0 (全零) |
| 主要用途 | 分配单个变量或结构体,或对初始化有特殊要求的数组 | 分配数组,尤其是希望默认值为 0 的数组 |
重要注意事项
- 检查返回值:每次调用
malloc或calloc后,必须检查返回值是否为NULL,这是防止程序因内存不足而崩溃的关键步骤。 - 释放内存:使用
malloc/calloc分配的内存,在不再使用时,必须使用free()函数释放,否则会导致内存泄漏。 - 避免重复释放:对同一块内存调用
free()两次是未定义行为,通常会导致程序崩溃。 - 释放后置空:
free(ptr)只是释放了内存,但指针变量ptr本身仍然存在,它仍然指向那块已释放的内存(这被称为“野指针”),为了避免误用,最好在free后立即将指针置为NULL:ptr = NULL;。 - 不要越界访问:动态分配的内存有固定的大小,访问超出这个范围的内存(数组越界)会导致未定义行为,可能引发程序崩溃或安全漏洞。
realloc的补充:如果需要调整已分配内存的大小,可以使用realloc函数,它会尝试在原地扩展内存,如果不行,则会分配一块新内存,并将旧数据复制过去,然后释放旧内存。
- 用
malloc:当你只需要一块内存,并且不关心它初始值是什么(或者你之后会立即覆盖它),或者你只想分配一个单一的结构体时。 - 用
calloc:当你明确需要分配一个数组,并且希望所有元素的初始值都为 0 时。calloc在这一点上提供了便利和安全性。
理解并正确使用 malloc 和 calloc 是掌握 C 语言动态内存管理的基石,也是编写健壮、高效 C 程序的必备技能。

(图片来源网络,侵删)

(图片来源网络,侵删)
