Skip to content

dynamic memory.c

文件信息

  • 📄 原文件:01_dynamic_memory.c
  • 🔤 语言:c

完整代码

c
// ============================================================
//                      动态内存管理
// ============================================================
// C 手动管理堆内存:malloc/calloc/realloc/free
// 【黄金法则】每个 malloc 必须有对应的 free
// 内存错误:泄漏、悬空指针、双重释放、越界访问
// Valgrind 是检测内存错误的利器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// ============================================================
//                      动态数组实现
// ============================================================

typedef struct {
    int   *data;     // 指向堆内存
    size_t size;     // 当前元素数
    size_t capacity; // 当前容量
} DynArray;

DynArray *dynarray_create(size_t initial_cap) {
    DynArray *da = (DynArray*)malloc(sizeof(DynArray));
    if (!da) return NULL;

    da->data = (int*)malloc(initial_cap * sizeof(int));
    if (!da->data) {
        free(da);
        return NULL;
    }
    da->size = 0;
    da->capacity = initial_cap;
    return da;
}

// 动态扩容(类似 C++ vector)
int dynarray_push(DynArray *da, int val) {
    if (da->size == da->capacity) {
        size_t new_cap = da->capacity * 2;
        int *new_data = (int*)realloc(da->data, new_cap * sizeof(int));
        if (!new_data) return -1;  // 扩容失败

        da->data = new_data;
        da->capacity = new_cap;
    }
    da->data[da->size++] = val;
    return 0;
}

void dynarray_print(const DynArray *da) {
    printf("[");
    for (size_t i = 0; i < da->size; i++)
        printf("%d%s", da->data[i], (i < da->size-1) ? ", " : "");
    printf("] size=%zu, cap=%zu\n", da->size, da->capacity);
}

void dynarray_destroy(DynArray *da) {
    if (da) {
        free(da->data);  // 先释放数据
        free(da);        // 再释放结构体
    }
}

// ============================================================
//                      内存池(简单实现)
// ============================================================

#define POOL_SIZE 1024

typedef struct {
    char   buffer[POOL_SIZE];
    size_t used;
} MemoryPool;

void pool_init(MemoryPool *pool) {
    pool->used = 0;
}

void *pool_alloc(MemoryPool *pool, size_t size) {
    // 对齐到 8 字节边界
    size_t aligned = (size + 7) & ~7;
    if (pool->used + aligned > POOL_SIZE) return NULL;

    void *ptr = pool->buffer + pool->used;
    pool->used += aligned;
    return ptr;
}

// 池内存一次性全部释放(重置)
void pool_reset(MemoryPool *pool) {
    pool->used = 0;
}

// ============================================================
//                      内存布局演示
// ============================================================

// 全局变量(BSS 段 / Data 段)
static int global_var = 100;
static int zero_var;  // 未初始化,BSS 段,自动清零

int main(void) {
    printf("=== 内存区域 ===\n");

    // 栈(Stack):局部变量,自动管理
    int stack_var = 42;

    // 堆(Heap):动态分配,手动管理
    int *heap_var = (int*)malloc(sizeof(int));
    *heap_var = 99;

    printf("全局变量地址(Data段):  %p\n", (void*)&global_var);
    printf("零初始化地址(BSS段):   %p\n", (void*)&zero_var);
    printf("栈变量地址(Stack):     %p\n", (void*)&stack_var);
    printf("堆变量地址(Heap):      %p\n", (void*)heap_var);
    printf("main 函数地址(Text段): %p\n", (void*)main);

    free(heap_var);
    heap_var = NULL;

    // ============================================================
    //                      malloc / calloc / realloc
    // ============================================================
    printf("\n=== malloc / calloc / realloc ===\n");

    // malloc:分配指定字节,内容未初始化
    int *arr = (int*)malloc(5 * sizeof(int));
    if (!arr) { fprintf(stderr, "malloc 失败\n"); return 1; }
    printf("malloc(未初始化): ");
    // 必须手动初始化,否则值不确定
    for (int i = 0; i < 5; i++) arr[i] = i * 10;
    for (int i = 0; i < 5; i++) printf("%d ", arr[i]);
    printf("\n");

    // calloc:分配并清零(内容全为 0)
    int *zarr = (int*)calloc(5, sizeof(int));
    if (!zarr) { free(arr); return 1; }
    printf("calloc(全为零): ");
    for (int i = 0; i < 5; i++) printf("%d ", zarr[i]);
    printf("\n");

    // realloc:调整大小(可能移动内存)
    int *big = (int*)realloc(arr, 10 * sizeof(int));
    if (!big) { free(arr); free(zarr); return 1; }
    arr = big;  // 更新指针(原指针可能已失效)
    for (int i = 5; i < 10; i++) arr[i] = i * 10;
    printf("realloc 到10个: ");
    for (int i = 0; i < 10; i++) printf("%d ", arr[i]);
    printf("\n");

    free(arr);   arr = NULL;
    free(zarr);  zarr = NULL;

    // ============================================================
    //                      动态数组演示
    // ============================================================
    printf("\n=== 动态数组 ===\n");

    DynArray *da = dynarray_create(4);
    printf("初始: "); dynarray_print(da);

    for (int i = 1; i <= 10; i++) {
        dynarray_push(da, i * i);
        if (i == 4 || i == 8 || i == 10)
            printf("push %d个后: ", i); dynarray_print(da);
    }

    dynarray_destroy(da);
    da = NULL;

    // ============================================================
    //                      二维动态数组
    // ============================================================
    printf("\n=== 二维动态数组 ===\n");

    int rows = 3, cols = 4;

    // 方法1:指针数组(每行独立分配)
    int **matrix = (int**)malloc(rows * sizeof(int*));
    for (int i = 0; i < rows; i++) {
        matrix[i] = (int*)malloc(cols * sizeof(int));
        for (int j = 0; j < cols; j++)
            matrix[i][j] = i * cols + j;
    }

    printf("矩阵(指针数组):\n");
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++)
            printf("%3d ", matrix[i][j]);
        printf("\n");
    }

    // 释放:先释放每行,再释放指针数组
    for (int i = 0; i < rows; i++) free(matrix[i]);
    free(matrix);

    // 方法2:一维数组模拟二维(连续内存,更高效)
    int *flat = (int*)malloc(rows * cols * sizeof(int));
    #define MAT(i,j) flat[(i)*cols + (j)]
    for (int i = 0; i < rows; i++)
        for (int j = 0; j < cols; j++)
            MAT(i,j) = i * cols + j;

    printf("矩阵(连续内存):\n");
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++)
            printf("%3d ", MAT(i,j));
        printf("\n");
    }
    free(flat);
    #undef MAT

    // ============================================================
    //                      内存池
    // ============================================================
    printf("\n=== 内存池 ===\n");

    MemoryPool pool;
    pool_init(&pool);

    // 从池中分配(零碎小分配效率高,无 malloc 系统调用)
    int *a = (int*)pool_alloc(&pool, sizeof(int));
    double *b = (double*)pool_alloc(&pool, sizeof(double));
    char *s = (char*)pool_alloc(&pool, 32);

    if (a && b && s) {
        *a = 42;
        *b = 3.14;
        strcpy(s, "池分配的字符串");
        printf("int=%d, double=%.2f, str=%s\n", *a, *b, s);
        printf("已使用: %zu / %d 字节\n", pool.used, POOL_SIZE);
    }

    pool_reset(&pool);  // 一次性全部释放
    printf("池已重置,已使用: %zu\n", pool.used);

    // ============================================================
    //                      常见内存错误演示(注释形式,不执行)
    // ============================================================
    printf("\n=== 内存错误(演示不执行)===\n");
    printf("1. 内存泄漏: malloc 后不 free\n");
    printf("   int *p = malloc(100); /* 忘记 free(p) */\n");
    printf("2. 悬空指针: free 后继续使用\n");
    printf("   free(p); *p = 1; /* 危险! */\n");
    printf("3. 双重释放: free 同一指针两次\n");
    printf("   free(p); free(p); /* 未定义行为! */\n");
    printf("4. 越界访问: int a[5]; a[5] = 1; /* 越界! */\n");
    printf("5. 栈溢出: 无限递归或超大局部数组\n");
    printf("\n检测工具: Valgrind, AddressSanitizer (-fsanitize=address)\n");

    printf("\n=== 动态内存演示完成 ===\n");
    return 0;
}

💬 讨论

使用 GitHub 账号登录后即可参与讨论

基于 MIT 许可发布