Clanguagememorymanagementtutorial

LearningClanguage memorymanagement, including静态memory分配, 动态memory分配, memory泄漏 避免 and memoryoptimization

返回tutoriallist

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, calloc or realloc分配 .

注意:

  • 不要释放已经释放 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, calloc or realloc都应该 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, usingcalloc而不 is malloc after 手动初始化.
  • 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.