核心概念
在循环中使用 malloc 的主要目的是根据循环的次数或条件,动态地创建一系列内存块,你可能需要为每个学生分配一个结构体,或者为每一行数据分配一个字符数组。

一个简单的示例:分配一个整数数组
假设我们想动态分配一个包含 n 个整数的数组。
#include <stdio.h>
#include <stdlib.h> // 必须包含这个头文件才能使用 malloc
int main() {
int n = 5;
int *numbers; // 声明一个整型指针
// 1. 分配一块连续的内存,可以存放 n 个 int
// sizeof(int) * n 计算总共需要的字节数
numbers = (int *)malloc(n * sizeof(int));
// 2. 检查 malloc 是否成功分配内存
// malloc 在失败时会返回 NULL 指针
if (numbers == NULL) {
printf("内存分配失败!\n");
return 1; // 返回非零表示错误
}
// 3. 使用循环来初始化这块内存
for (int i = 0; i < n; i++) {
numbers[i] = i * 10; // 像使用普通数组一样使用指针
printf("numbers[%d] = %d\n", i, numbers[i]);
}
// 4. 释放内存!非常重要!
// 将之前分配的内存归还给系统
free(numbers);
// 5. 将指针设为 NULL,这是一个好习惯,可以防止“悬垂指针”
numbers = NULL;
return 0;
}
输出:
numbers[0] = 0
numbers[1] = 10
numbers[2] = 20
numbers[3] = 30
numbers[4] = 40
在循环中为多个对象分配内存(更常见的场景)
这才是 malloc 循环的真正威力所在,我们通常为循环的每一次迭代分配一个独立的内存块。
场景: 我们想存储5个学生的姓名,每个姓名的长度不同。

#include <stdio.h>
#include <stdlib.h>
#include <string.h> // 使用 strcpy
int main() {
int num_students = 5;
char *student_names[num_students]; // 声明一个指针数组,每个元素都是一个 char*
// 分配阶段:在循环中为每个学生分配内存
for (int i = 0; i < num_students; i++) {
// 假设我们为每个名字分配 20 个字符的空间 (+1 给 '\0')
student_names[i] = (char *)malloc(20 * sizeof(char));
// 检查分配是否成功
if (student_names[i] == NULL) {
printf("为 student_names[%d] 分配内存失败!\n", i);
// 如果分配失败,我们应该释放之前已经分配的所有内存
for (int j = 0; j < i; j++) {
free(student_names[j]);
}
return 1;
}
}
// 使用阶段:在循环中写入数据
strcpy(student_names[0], "Zhang San");
strcpy(student_names[1], "Li Si");
strcpy(student_names[2], "Wang Wu");
strcpy(student_names[3], "Zhao Liu");
strcpy(student_names[4], "Qian Qi");
// 打印阶段:在循环中读取数据
printf("学生名单:\n");
for (int i = 0; i < num_students; i++) {
printf("%d: %s\n", i + 1, student_names[i]);
}
// 释放阶段:在循环中释放所有分配的内存
// 这是至关重要的一步,否则会造成严重的内存泄漏!
printf("\n正在释放内存...\n");
for (int i = 0; i < num_students; i++) {
free(student_names[i]); // 释放每个指针指向的内存块
student_names[i] = NULL; // 将指针置为 NULL
}
return 0;
}
输出:
学生名单:
1: Zhang San
2: Li Si
3: Wang Wu
4: Zhao Liu
5: Qian Qi
正在释放内存...
关键点和最佳实践
-
检查返回值:
malloc并不总是成功,在内存不足时,它会返回NULL。永远不要忽略这个检查,否则解引用一个NULL指针会导致程序崩溃(段错误)。 -
计算所需大小:使用
sizeof(你的数据类型)来确保分配了正确数量的字节。malloc(10)只分配10字节,可能不够放一个整数,而malloc(10 * sizeof(int))则会分配足够存放10个整数的空间。 -
释放内存:
malloc分配的内存不会自动释放,它存在于程序的堆上,直到你显式地调用free()来释放它。忘记free是导致内存泄漏最常见的原因,内存泄漏会使程序在长时间运行后消耗越来越多的内存,最终导致系统变慢或崩溃。
(图片来源网络,侵删) -
匹配
free和malloc:你用malloc分配的内存,必须用free来释放,不能用free释放静态变量、栈上的变量或其他函数(如calloc)分配的内存(除非它们是兼容的)。 -
避免重复释放:千万不要对一个内存块调用两次
free,这会导致“双重释放”错误,同样会引发程序崩溃或未定义行为,将指针置为NULL(ptr = NULL;) 可以防止这种情况,因为free(NULL)是安全的(它什么都不做)。 -
不要越界访问:分配了多少内存,就只能使用多少,如果你为一个10个整数的数组分配了空间,就不要尝试访问
array[10],这会导致缓冲区溢出,同样是严重的错误。
常见错误示例
错误1:忘记检查 malloc 的返回值
int *p = (int *)malloc(1000000000 * sizeof(int)); // 尝试分配超大内存 *p = 10; // p 是 NULL,这里会直接导致程序崩溃
错误2:内存泄漏
void function_with_leak() {
int *p = (int *)malloc(sizeof(int));
*p = 100;
// 函数结束时,p 被销毁,但它指向的内存块没有被释放
// 这块内存就“泄漏”了,再也找不回来
}
int main() {
for (int i = 0; i < 10000; i++) {
function_with_leak(); // 每次调用都会泄漏一点内存
}
return 0;
}
错误3:双重释放
int *p = (int *)malloc(sizeof(int)); free(p); free(p); // 第二次 free 是错误的!
在循环中使用 malloc 是C语言中处理动态数据结构(如链表、动态数组、树等)的基础,记住这个黄金法则:
对于每一次
malloc,都应有一次对应的free。
并且始终牢记:
- 检查
NULL。 - 计算正确的
sizeof。 - 在循环结束时,记得在循环中
free所有分配的内存。
