C 语言允许你手动管理内存。这很强大,但也需要小心使用。
程序运行时,内存分为几个区域:
┌─────────────────────┐
│ 栈 (Stack) │ ← 局部变量,自动管理
├─────────────────────┤
│ ↓ │
│ | │
│ ↓ │
├─────────────────────┤
│ 堆 (Heap) │ ← 动态分配,手动管理
├─────────────────────┤
│ 全局/静态区 │ ← 全局变量
├─────────────────────┤
│ 代码区 │ ← 程序代码
└─────────────────────┘
void function(void) {
int x = 10; // 栈上分配
float arr[100]; // 栈上分配
// ... 函数执行
} // 函数结束,自动释放
特点:
#include <stdlib.h>
// 分配内存
int* ptr = malloc(sizeof(int) * 100); // 分配 100 个 int
// 使用内存
ptr[0] = 10;
ptr[99] = 100;
// 释放内存
free(ptr);
ptr = NULL; // 好习惯:释放后置为 NULL
特点:
#include <stdlib.h>
// 分配单个 int
int* num = malloc(sizeof(int));
*num = 42;
// 分配数组
int* arr = malloc(sizeof(int) * 10);
for (int i = 0; i < 10; i++) {
arr[i] = i;
}
// 分配结构体
typedef struct {
int x, y;
} Point;
Point* p = malloc(sizeof(Point));
p->x = 10;
p->y = 20;
free(num);
free(arr);
free(p);
// malloc:不初始化,内容是随机值
int* arr1 = malloc(sizeof(int) * 10); // 内容未知
// calloc:初始化为 0
int* arr2 = calloc(10, sizeof(int)); // 所有元素 = 0
// 等价于
int* arr3 = malloc(sizeof(int) * 10);
memset(arr3, 0, sizeof(int) * 10);
// 初始分配
int* arr = malloc(sizeof(int) * 5);
// ... 使用中发现不够用
// 调整大小为 10
int* new_arr = realloc(arr, sizeof(int) * 10);
// realloc 可能返回新地址
// 原来的数据会被复制过去
if (new_arr) {
arr = new_arr;
}
// 使用完释放
free(arr);
typedef struct Segment {
int x, y;
struct Segment* next;
} Segment;
typedef struct {
Segment* head;
int length;
} Snake;
// 创建蛇
Snake* snake_create(int start_x, int start_y) {
Snake* snake = malloc(sizeof(Snake));
Segment* head = malloc(sizeof(Segment));
head->x = start_x;
head->y = start_y;
head->next = NULL;
snake->head = head;
snake->length = 1;
return snake;
}
// 添加身体段
void snake_grow(Snake* snake) {
Segment* new_segment = malloc(sizeof(Segment));
new_segment->next = snake->head;
snake->head = new_segment;
snake->length++;
}
// 销毁蛇(重要!)
void snake_destroy(Snake* snake) {
Segment* current = snake->head;
// 遍历链表,释放每个段
while (current) {
Segment* next = current->next;
free(current);
current = next;
}
free(snake);
}
int main(void) {
// 创建蛇
Snake* snake = snake_create(10, 10);
// 游戏循环
while (game_running) {
// ... 游戏逻辑
}
// 清理内存(重要!)
snake_destroy(snake);
return 0;
}
void leak_memory(void) {
int* ptr = malloc(sizeof(int) * 100);
// ... 使用
// ❌ 忘记 free,内存泄漏!
}
int* ptr = malloc(sizeof(int));
free(ptr);
free(ptr); // ❌ 重复释放,未定义行为!
int* ptr = malloc(sizeof(int));
*ptr = 10;
free(ptr);
*ptr = 20; // ❌ 野指针!
int* arr = malloc(sizeof(int) * 10);
arr[0] = 1;
arr[9] = 10;
arr[10] = 11; // ❌ 越界!
Snake* create_snake(void) {
Snake* s = malloc(sizeof(Snake));
return s;
}
void destroy_snake(Snake* s) {
free(s);
}
// 使用
Snake* s = create_snake();
// ... 使用
destroy_snake(s); // 配对释放
free(ptr);
ptr = NULL; // 防止野指针
int* ptr = malloc(sizeof(int) * 100);
if (!ptr) {
fprintf(stderr, "内存分配失败!\n");
return -1;
}
# 使用 valgrind 检测内存问题
valgrind --leak-check=full ./snake_game
创建一个动态数组,支持添加元素和扩容
实现一个函数,复制字符串(使用 malloc 分配新内存)
用 valgrind 检查你的贪吃蛇程序是否有内存泄漏
下一课:链表