1. memorymanagementoverview
1.1 what is memorymanagement
memorymanagement is 指程序 for 计算机memoryresource 分配, using and 释放 过程. in Clanguagein, memorymanagement is a 非常 important 主题, 因 for Clanguage不像otheradvancedlanguage (such asJava, Python) 那样 has 自动 memorymanagementmechanism (垃圾回收器) , 而 is 需要程序员手动managementmemory.
has 效 memorymanagement可以:
- improving程序 performance
- reducingmemoryusing
- 避免memory泄漏
- 防止程序崩溃
1.2 Clanguagein memory区域
in Clanguagein, 程序 memory空间通常被划分 for 以 under 几个区域:
- code区 (Text Segment) : store程序 可执行指令.
- 全局/静态区 (Data Segment) : store全局variable and 静态variable.
- 常量区 (Constant Segment) : store常量stringetc..
- 堆区 (Heap) : 动态memory分配 区域, 由程序员手动management.
- 栈区 (Stack) : store局部variable, functionparameter and 返回地址, 由编译器自动management.
1.3 memory分配方式
Clanguagein memory分配方式主要 has 两种:
- 静态memory分配: in 编译时分配memory, such as全局variable, 静态variable and 局部variable.
- 动态memory分配: in 程序run时分配memory, such asusingmalloc, calloc, reallocetc.function分配 memory.
2. 静态memory分配
2.1 全局variable and 静态variable
全局variable and 静态variablestore in 全局/静态区, 它们 memory空间 in 程序开始执行时分配, in 程序结束时释放.
// 全局variable, store in 全局/静态区
int global_var = 100;
void func() {
// 静态variable, store in 全局/静态区
static int static_var = 200;
// 局部variable, store in 栈区
int local_var = 300;
printf("global_var = %d\n", global_var);
printf("static_var = %d\n", static_var);
printf("local_var = %d\n", local_var);
// modify静态variable 值
static_var++;
}
int main() {
func(); // 输出: global_var = 100, static_var = 200, local_var = 300
func(); // 输出: global_var = 100, static_var = 201, local_var = 300
return 0;
}
2.2 静态memory分配 特点
- memory分配 in 编译时completion, 程序run时不需要再分配.
- memory空间 in 程序开始执行时分配, in 程序结束时释放.
- 全局variable and 静态variable 初始化值 for 0 (such as果没 has 显式初始化) .
- 局部variable (自动variable) store in 栈区, 它们 memory空间 in function调用时分配, in function返回时释放.
- 局部variablesuch as果没 has 显式初始化, 其值 is 不确定 (垃圾值) .
3. 动态memory分配
3.1 动态memory分配 concepts
动态memory分配 is 指 in 程序run时根据需要分配memory空间 过程. in Clanguagein, 动态memory分配主要through以 under 几个functionimplementation:
malloc: 分配指定 big small memory空间.calloc: 分配指定数量 and big small memory空间, 并初始化 for 0.realloc: reassignmemory空间, 用于扩 big or 缩 small 已分配 memory.free: 释放已分配 memory空间.
这些function都定义 in stdlib.h头filein.
3.2 mallocfunction
void *malloc(size_t size);
其in:
size: 要分配 memory big small , 以字节 for 单位.- return value: 成功时返回指向分配memory 指针, 失败时返回
NULL.
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
int n = 5;
// 分配memory
ptr = (int *)malloc(n * sizeof(int));
// checkmemory分配 is 否成功
if (ptr == NULL) {
printf("memory分配失败\n");
return 1;
}
// using分配 memory
printf("请输入5个整数: ");
for (int i = 0; i < n; i++) {
scanf("%d", &ptr[i]);
}
printf("你输入 整数 is : ");
for (int i = 0; i < n; i++) {
printf("%d ", ptr[i]);
}
printf("\n");
// 释放memory
free(ptr);
return 0;
}
3.3 callocfunction
void *calloc(size_t count, size_t size);
其in:
count: 要分配 memory块数量.size: 每个memory块 big small , 以字节 for 单位.- return value: 成功时返回指向分配memory 指针, 失败时返回
NULL.
and malloc不同, calloc会将分配 memory初始化 for 0.
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
int n = 5;
// 分配memory并初始化 for 0
ptr = (int *)calloc(n, sizeof(int));
// checkmemory分配 is 否成功
if (ptr == NULL) {
printf("memory分配失败\n");
return 1;
}
// 输出初始值 (应该都 is 0)
printf("初始值: ");
for (int i = 0; i < n; i++) {
printf("%d ", ptr[i]);
}
printf("\n");
// using分配 memory
printf("请输入5个整数: ");
for (int i = 0; i < n; i++) {
scanf("%d", &ptr[i]);
}
printf("你输入 整数 is : ");
for (int i = 0; i < n; i++) {
printf("%d ", ptr[i]);
}
printf("\n");
// 释放memory
free(ptr);
return 0;
}
3.4 reallocfunction
void *realloc(void *ptr, size_t size);
其in:
ptr: 指向之 before 分配 memory 指针.size: new memory big small , 以字节 for 单位.- return value: 成功时返回指向reassignmemory 指针, 失败时返回
NULL, 原memory保持不变.
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
int n = 5, m = 10;
// 分配memory
ptr = (int *)malloc(n * sizeof(int));
// checkmemory分配 is 否成功
if (ptr == NULL) {
printf("memory分配失败\n");
return 1;
}
// using分配 memory
printf("请输入5个整数: ");
for (int i = 0; i < n; i++) {
scanf("%d", &ptr[i]);
}
// reassignmemory (扩 big )
ptr = (int *)realloc(ptr, m * sizeof(int));
// checkmemoryreassign is 否成功
if (ptr == NULL) {
printf("memoryreassign失败\n");
return 1;
}
// 输入更 many 整数
printf("请再输入5个整数: ");
for (int i = 5; i < m; i++) {
scanf("%d", &ptr[i]);
}
// 输出所 has 整数
printf("你输入 10个整数 is : ");
for (int i = 0; i < m; i++) {
printf("%d ", ptr[i]);
}
printf("\n");
// 释放memory
free(ptr);
return 0;
}
3.5 freefunction
void free(void *ptr);
其in:
ptr: 指向要释放 memory 指针, 该指针必须 is 之 before throughmalloc,callocorrealloc分配 .
注意:
- 不要释放已经释放 memory, 否则会导致undefinedbehavior.
- 不要释放栈 on memory, 只能释放堆 on memory.
- 释放memory after , 最 good 将指针设置 for
NULL, 以避免野指针.
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
// 分配memory
ptr = (int *)malloc(5 * sizeof(int));
// checkmemory分配 is 否成功
if (ptr == NULL) {
printf("memory分配失败\n");
return 1;
}
// using分配 memory
for (int i = 0; i < 5; i++) {
ptr[i] = i + 1;
}
// 释放memory
free(ptr);
// 将指针设置 for NULL, 避免野指针
ptr = NULL;
return 0;
}
4. memory泄漏
4.1 what is memory泄漏
memory泄漏 is 指程序 in run过程in分配了memory, 但 in 不再需要时没 has 释放, 导致memoryresource被浪费 现象. memory泄漏会导致程序 memoryusing量continuously增加, 最终可能导致程序崩溃 or systemperformance under 降.
4.2 memory泄漏 例子
#include <stdio.h>
#include <stdlib.h>
void func() {
int *ptr;
// 分配memory
ptr = (int *)malloc(100 * sizeof(int));
// usingmemory...
// 没 has 释放memory, 导致memory泄漏
}
int main() {
// many 次调用funcfunction, 导致 many 次memory泄漏
for (int i = 0; i < 1000; i++) {
func();
}
return 0;
}
4.3 such as何避免memory泄漏
- 始终释放不再using memory.
- usingmemory分配 and 释放 配 for principles: 每一个
malloc,callocorrealloc都应该 has for 应free. - in functionin分配memory时, 确保 in 所 has 返回path on 都释放memory.
- usingmemorymanagementtool (such asValgrind) 检测memory泄漏.
- adoptsRAII (resource获取即初始化) 模式, 虽然Clanguage没 has 原生support, 但可以through宏 or function来mock.
5. memory分配 errorprocessing
5.1 检测memory分配失败
in Clanguagein, 当memory分配失败时, malloc, calloc and reallocfunction会返回NULL. 因此, in using这些function分配memory after , 应该始终checkreturn value is 否 for NULL.
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
// 分配memory
ptr = (int *)malloc(1000000000 * sizeof(int)); // 尝试分配 big 量memory
// checkmemory分配 is 否成功
if (ptr == NULL) {
printf("memory分配失败\n");
return 1;
}
// using分配 memory...
// 释放memory
free(ptr);
return 0;
}
5.2 processingmemory分配失败 circumstances
当memory分配失败时, 可以采取以 under 措施:
- 输出errorinformation, 提示usermemory不足.
- 释放other不再using memory, 然 after 重 new 尝试分配.
- 降 low memoryusingrequirements, 例such asreducing分配 memory big small .
- 程序优雅退出, 避免崩溃.
6. memoryoptimization
6.1 memoryusing best practices
- 只分配必要 memory.
- 及时释放不再using memory.
- 避免频繁 memory分配 and 释放, 特别 is in 循环in.
- using适当 memory分配function, 例such as for 于需要初始化 for 0 memory, using
calloc而不 ismallocafter 手动初始化. - for 于 small memory分配, 考虑usingmemory池.
6.2 memory for 齐
memory for 齐 is 指data in memoryin store位置按照一定 规则排列, 以improvingmemory访问efficiency. in Clanguagein, 可以usingalignof运算符获取class型 for 齐要求, using__attribute__((aligned(n))) or #pragma pack(n)指令控制memory for 齐.
#include <stdio.h>
int main() {
// basicclass型 for 齐要求
printf("char for 齐要求: %zu字节\n", alignof(char));
printf("int for 齐要求: %zu字节\n", alignof(int));
printf("double for 齐要求: %zu字节\n", alignof(double));
// structure体 for 齐
struct S1 {
char c;
int i;
};
struct S2 {
int i;
char c;
};
printf("structure体S1 big small : %zu字节\n", sizeof(struct S1));
printf("structure体S2 big small : %zu字节\n", sizeof(struct S2));
return 0;
}
6.3 memory池
memory池 is amemorymanagementtechniques, 它预先分配一块较 big memory, 然 after in 需要时 from 这块memoryin分配 small 块memory, 而不 is 每次都调用mallocetc.function. memory池可以reducingmemory分配 开销, improvingmemory分配 efficiency, 同时也可以reducingmemory碎片.
memory池 basicimplementationapproach:
- 初始化时分配一块较 big memory.
- maintenance一个空闲memory块 链表.
- 当需要分配memory时, from 空闲链表in找 to 合适 memory块.
- 当释放memory时, 将memory块放回空闲链表.
- 当memory池用完时, 可以scalememory池 or 返回error.
7. 实践case
7.1 using动态memory分配implementation链表
#include <stdio.h>
#include <stdlib.h>
// 定义链表nodestructure体
typedef struct Node {
int data;
struct Node *next;
} Node;
// creation new node
Node *createNode(int data) {
Node *newNode = (Node *)malloc(sizeof(Node));
if (newNode == NULL) {
printf("memory分配失败\n");
return NULL;
}
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// in 链表末尾添加node
void addNode(Node **head, int data) {
Node *newNode = createNode(data);
if (newNode == NULL) {
return;
}
if (*head == NULL) {
*head = newNode;
return;
}
Node *temp = *head;
while (temp->next != NULL) {
temp = temp->next;
}
temp->next = newNode;
}
// 显示链表
void displayList(Node *head) {
if (head == NULL) {
printf("链表 for 空\n");
return;
}
Node *temp = head;
printf("链表 in 容: ");
while (temp != NULL) {
printf("%d ", temp->data);
temp = temp->next;
}
printf("\n");
}
// 释放链表
void freeList(Node *head) {
Node *temp;
while (head != NULL) {
temp = head;
head = head->next;
free(temp);
}
}
int main() {
Node *head = NULL;
// 添加node
addNode(&head, 10);
addNode(&head, 20);
addNode(&head, 30);
addNode(&head, 40);
addNode(&head, 50);
// 显示链表
displayList(head);
// 释放链表
freeList(head);
return 0;
}
7.2 using动态memory分配implementation可变 big small array
#include <stdio.h>
#include <stdlib.h>
// 可变 big small arraystructure体
typedef struct {
int *data;
int size;
int capacity;
} DynamicArray;
// 初始化动态array
void initArray(DynamicArray *arr, int initialCapacity) {
arr->data = (int *)malloc(initialCapacity * sizeof(int));
if (arr->data == NULL) {
printf("memory分配失败\n");
arr->size = 0;
arr->capacity = 0;
return;
}
arr->size = 0;
arr->capacity = initialCapacity;
}
// 添加元素 to 动态array
void addElement(DynamicArray *arr, int element) {
// check is 否需要扩容
if (arr->size >= arr->capacity) {
// 扩容 for 原来 2倍
int newCapacity = arr->capacity * 2;
if (newCapacity == 0) {
newCapacity = 1;
}
int *newData = (int *)realloc(arr->data, newCapacity * sizeof(int));
if (newData == NULL) {
printf("memoryreassign失败\n");
return;
}
arr->data = newData;
arr->capacity = newCapacity;
}
// 添加元素
arr->data[arr->size] = element;
arr->size++;
}
// 显示动态array
void displayArray(DynamicArray *arr) {
printf("动态array in 容: ");
for (int i = 0; i < arr->size; i++) {
printf("%d ", arr->data[i]);
}
printf("\n");
printf("当 before big small : %d, 容量: %d\n", arr->size, arr->capacity);
}
// 释放动态array
void freeArray(DynamicArray *arr) {
free(arr->data);
arr->data = NULL;
arr->size = 0;
arr->capacity = 0;
}
int main() {
DynamicArray arr;
// 初始化动态array
initArray(&arr, 2);
// 添加元素
for (int i = 1; i <= 10; i++) {
addElement(&arr, i * 10);
displayArray(&arr);
}
// 释放动态array
freeArray(&arr);
return 0;
}
实践case: memory池 simple implementation
writing一个C程序, implementation一个 simple memory池, 用于management small memory 分配 and 释放, reducingmemory分配 开销.
requirementsanalysis
- 初始化memory池, 分配一块较 big memory.
- from memory池in分配 small memory块.
- 将不再using memory块放回memory池.
- 销毁memory池, 释放所 has 分配 memory.
referencecode
#include <stdio.h>
#include <stdlib.h>
// memory块structure体
typedef struct MemoryBlock {
struct MemoryBlock *next;
char data[0]; // 柔性array, 用于storepracticaldata
} MemoryBlock;
// memory池structure体
typedef struct {
MemoryBlock *freeList;
size_t blockSize;
size_t poolSize;
char *pool;
} MemoryPool;
// 初始化memory池
void initPool(MemoryPool *pool, size_t blockSize, size_t blockCount) {
// 计算memory池 big small
size_t totalSize = blockCount * (blockSize + sizeof(MemoryBlock));
// 分配memory池
pool->pool = (char *)malloc(totalSize);
if (pool->pool == NULL) {
printf("memory池初始化失败\n");
pool->freeList = NULL;
pool->blockSize = 0;
pool->poolSize = 0;
return;
}
pool->blockSize = blockSize;
pool->poolSize = totalSize;
pool->freeList = NULL;
// 初始化空闲memory块链表
for (size_t i = 0; i < blockCount; i++) {
MemoryBlock *block = (MemoryBlock *)(pool->pool + i * (blockSize + sizeof(MemoryBlock)));
block->next = pool->freeList;
pool->freeList = block;
}
printf("memory池初始化成功, 块 big small : %zu字节, 块数量: %zu\n", blockSize, blockCount);
}
// from memory池in分配memory
void *allocFromPool(MemoryPool *pool) {
if (pool->freeList == NULL) {
printf("memory池无可用memory\n");
return NULL;
}
// from 空闲链表in取出一个memory块
MemoryBlock *block = pool->freeList;
pool->freeList = block->next;
return block->data;
}
// 将memory块放回memory池
void freeToPool(MemoryPool *pool, void *ptr) {
if (ptr == NULL) {
return;
}
// 计算memory块 起始地址
MemoryBlock *block = (MemoryBlock *)((char *)ptr - sizeof(MemoryBlock));
// 将memory块添加 to 空闲链表
block->next = pool->freeList;
pool->freeList = block;
}
// 销毁memory池
void destroyPool(MemoryPool *pool) {
if (pool->pool != NULL) {
free(pool->pool);
pool->pool = NULL;
}
pool->freeList = NULL;
pool->blockSize = 0;
pool->poolSize = 0;
printf("memory池已销毁\n");
}
int main() {
MemoryPool pool;
// 初始化memory池, 每个块 big small for 32字节, 共10个块
initPool(&pool, 32, 10);
// 分配memory
void *ptr1 = allocFromPool(&pool);
void *ptr2 = allocFromPool(&pool);
void *ptr3 = allocFromPool(&pool);
printf("分配 memory地址: %p, %p, %p\n", ptr1, ptr2, ptr3);
// usingmemory
sprintf((char *)ptr1, "Hello");
sprintf((char *)ptr2, "World");
sprintf((char *)ptr3, "Memory Pool");
printf("memory in 容: %s, %s, %s\n", (char *)ptr1, (char *)ptr2, (char *)ptr3);
// 释放memory
freeToPool(&pool, ptr1);
freeToPool(&pool, ptr2);
freeToPool(&pool, ptr3);
printf("memory已释放回memory池\n");
// 再次分配memory
void *ptr4 = allocFromPool(&pool);
sprintf((char *)ptr4, "Reused Memory");
printf("reassign memory地址: %p, in 容: %s\n", ptr4, (char *)ptr4);
// 释放memory
freeToPool(&pool, ptr4);
// 销毁memory池
destroyPool(&pool);
return 0;
}
互动练习
练习1: writing一个C程序, using动态memory分配implementation一个二维array, support动态调整 big small .
提示: using双重指针, 首先分配行指针array, 然 after for 每行分配列元素.
练习2: writing一个C程序, 检测并修复memory泄漏.
提示: analysis以 under code, 找出memory泄漏 地方, 并修复它.
void leakyFunction() {
int *ptr = (int *)malloc(100 * sizeof(int));
// usingptr...
// 没 has 释放ptr
}
int main() {
for (int i = 0; i < 1000; i++) {
leakyFunction();
}
return 0;
}
练习3: writing一个C程序, usingmemory池managementstring 分配 and 释放.
提示: implementation一个stringmemory池, 用于分配 and 释放固定 big small string.