5. 动态内存分配 - <stdlib.h>
在 C 语言中,动态内存分配允许程序在运行时请求内存空间,增加了灵活性以适应内存使用的动态需求。stdlib.h
提供了一组函数用于实现这一功能,包括 malloc
、calloc
、realloc
和 free
。
5.1. 基础动态内存操作
5.1.1. malloc
和 calloc
-
malloc
(memory allocation)- 作用:分配指定字节大小的内存块,但不初始化内存。
- 用法:
int *array; array = (int *)malloc(10 * sizeof(int)); // 分配10个int大小的内存
- 注意事项:若内存分配失败,
malloc
返回NULL
,应检查返回指针是否为NULL
。
-
calloc
(contiguous allocation)- 作用:分配内存并初始化为零。
- 用法:
int *array; array = (int *)calloc(10, sizeof(int)); // 分配并初始化10个int大小的内存
- 优点:自动将分配的内存初始化为零,防止潜在的未初始化问题。
5.1.2. realloc
和 free
-
realloc
(reallocation)- 作用:调整之前已分配内存块的大小。
- 用法:
array = (int *)realloc(array, 20 * sizeof(int)); // 调整内存大小为20个int
- 注意事项:可以收缩或扩展内存块;在扩展时可能会移动内存块,因此一定要用新指针接收返回值。
-
free
- 作用:释放之前分配的内存。
- 用法:
free(array); // 释放内存
- 注意事项:释放后应将指针置
NULL
以避免“悬挂指针”问题。
5.2. 内存分配策略与最佳实践
5.2.1. 避免内存泄露
- 内存泄露:在程序中未释放不再需要的内存,从而导致内存逐渐减少。
- 避免策略:
- 每次
malloc
或calloc
后,即使在异常退出情况下,也应确保调用free
。 - 避免在局部作用域的
if
、switch
或loop
中分配的内存没有被释放。 - 使用工具如 Valgrind 检查内存泄露。
- 每次
5.2.2. 内存对齐及其重要性
- 内存对齐:保证数据在内存中按特定字节边界排列,这是很多系统所需要的。
- 重要性:
- 提高程序执行效率,因为许多处理器读取内存时要求数据对齐。
- 不对齐的数据访问可能导致性能下降,甚至在某些架构上导致程序崩溃。
以下是一个示例,展示了动态内存分配和释放操作:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *data;
// 分配10个整数的数组
data = (int *)malloc(10 * sizeof(int));
if (data == NULL) {
perror("Allocation failed");
return 1;
}
// 使用数据
for (int i = 0; i < 10; i++) {
data[i] = i * 10;
printf("%d ", data[i]);
}
// 调整大小
data = (int *)realloc(data, 20 * sizeof(int));
if (data == NULL) {
perror("Reallocation failed");
return 1;
}
// 继续使用扩展后的数据
for (int i = 10; i < 20; i++) {
data[i] = i * 10;
printf("%d ", data[i]);
}
// 释放内存
free(data);
data = NULL; // 避免悬挂指针
return 0;
}
在上面的代码中,我们展示了如何使用 malloc
、realloc
和 free
来管理动态内存,包括分配、调整大小和释放操作。在整个操作中,确保检查内存分配是否成功,并在释放后将指针置 NULL
以保护程序的稳定性。