11.1 动态内存分配
在C语言中,动态内存管理主要通过以下函数实现:
malloc(size_t size)
:分配指定大小的内存块,返回指向该内存块的指针。内存块的初始内容是未定义的。calloc(size_t num, size_t size)
:分配一个内存块,能够容纳指定数量的对象,每个对象的大小为指定的字节数。分配的内存块会被初始化为零。realloc(void *ptr, size_t size)
:调整已经分配的内存块的大小。如果新大小大于原大小,新分配的内存会被初始化为零。返回新的内存块的指针。free(void *ptr)
:释放先前分配的内存块。
示例:
#include <stdio.h>
#include <stdlib.h>
int main() {
// 使用 malloc 分配内存
int *arr = (int *)malloc(5 * sizeof(int));
if (arr == NULL) {
printf("Memory allocation failed\n");
return 1;
}
// 使用分配的内存
for (int i = 0; i < 5; i++) {
arr[i] = i * 2;
}
// 打印数组内容
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// 释放内存
free(arr);
return 0;
}
11.2 动态数组
1. 动态数组的创建和使用
可以使用 malloc
或 calloc
创建动态数组,并使用 realloc
调整其大小。
示例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr;
int initialSize = 5;
int newSize = 10;
// 创建动态数组
arr = (int *)malloc(initialSize * sizeof(int));
if (arr == NULL) {
printf("Memory allocation failed\n");
return 1;
}
// 初始化数组
for (int i = 0; i < initialSize; i++) {
arr[i] = i * 2;
}
// 调整数组大小
arr = (int *)realloc(arr, newSize * sizeof(int));
if (arr == NULL) {
printf("Memory reallocation failed\n");
return 1;
}
// 添加新元素
for (int i = initialSize; i < newSize; i++) {
arr[i] = i * 2;
}
// 打印数组内容
for (int i = 0; i < newSize; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// 释放内存
free(arr);
return 0;
}
11.3 动态内存管理的注意事项
1. 内存泄漏
内存泄漏是指程序在运行过程中分配了内存但没有释放,导致内存被浪费。为了防止内存泄漏,确保每个 malloc
或 calloc
调用都对应一个 free
调用。
2. 双重释放
避免对同一块内存进行多次释放,这会导致程序崩溃或不可预测的行为。
3. 野指针
释放内存后,将指针设为 NULL
,以防止它成为野指针,即指向已经释放的内存区域。
示例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int *)malloc(sizeof(int));
if (ptr == NULL) {
printf("Memory allocation failed\n");
return 1;
}
*ptr = 42;
printf("Value: %d\n", *ptr);
free(ptr);
ptr = NULL; // 避免野指针
// 以下代码会导致未定义行为,因为 ptr 已被释放
// printf("Value: %d\n", *ptr);
return 0;
}
11.4 自定义内存管理
有时,可能需要实现自己的内存分配器,以更好地控制内存管理策略。这通常涉及实现自定义的分配和释放函数,或者维护一个内存池来管理内存。
示例:
#include <stdio.h>
#include <stdlib.h>
#define POOL_SIZE 1024
typedef struct {
char pool[POOL_SIZE];
size_t offset;
} MemoryPool;
void initPool(MemoryPool *mp) {
mp->offset = 0;
}
void *poolAlloc(MemoryPool *mp, size_t size) {
if (mp->offset + size > POOL_SIZE) {
return NULL; // 内存池不足
}
void *ptr = mp->pool + mp->offset;
mp->offset += size;
return ptr;
}
int main() {
MemoryPool mp;
initPool(&mp);
int *arr = (int *)poolAlloc(&mp, 5 * sizeof(int));
if (arr == NULL) {
printf("Memory pool allocation failed\n");
return 1;
}
for (int i = 0; i < 5; i++) {
arr[i] = i * 2;
}
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
练习
- 编写一个程序,使用动态内存分配创建一个整数数组,允许用户输入数组的大小和元素,然后计算并输出数组的平均值。
- 编写一个程序,创建一个动态二维数组,并对其进行初始化和输出。
- 实现一个简单的内存池管理器,模拟内存的分配和释放,管理一个固定大小的内存区域。
- 编写一个程序,检测并报告内存泄漏(可以使用工具如 Valgrind 来帮助检测)。
完成这些练习后,你将掌握C语言的动态内存管理。