Administrator
Administrator
发布于 2024-09-05 / 13 阅读
0
0

第11章:动态内存管理

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. 动态数组的创建和使用

可以使用 malloccalloc 创建动态数组,并使用 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. 内存泄漏

内存泄漏是指程序在运行过程中分配了内存但没有释放,导致内存被浪费。为了防止内存泄漏,确保每个 malloccalloc 调用都对应一个 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;
}

练习

  1. 编写一个程序,使用动态内存分配创建一个整数数组,允许用户输入数组的大小和元素,然后计算并输出数组的平均值。
  2. 编写一个程序,创建一个动态二维数组,并对其进行初始化和输出。
  3. 实现一个简单的内存池管理器,模拟内存的分配和释放,管理一个固定大小的内存区域。
  4. 编写一个程序,检测并报告内存泄漏(可以使用工具如 Valgrind 来帮助检测)。

完成这些练习后,你将掌握C语言的动态内存管理。


评论