21. 内存管理函数.md 6.5 KB

21 内存管理函数

21.1 完整概念讲解

堆内存管理

C语言通过标准库函数管理堆内存:

函数 功能 特点
malloc 分配内存 不初始化,内容随机
calloc 分配并清零 初始化为0
realloc 调整大小 可能迁移内存位置
free 释放内存 必须使用分配的指针

内存操作函数

函数 功能 注意事项
memcpy 内存复制 不处理重叠
memmove 内存移动 处理重叠情况
memset 内存设置 按字节设置
memcmp 内存比较 按字节比较

栈内存 vs 堆内存

特性 栈内存 堆内存
分配速度 快(移动栈指针) 慢(系统调用)
大小限制 较小(通常几MB) 较大(受物理内存限制)
生命周期 函数结束自动释放 需手动free
碎片问题 可能产生碎片

嵌入式内存池

嵌入式系统中常用内存池(Memory Pool)预分配固定大小的内存块,避免动态分配的不确定性和碎片问题。

21.2 核心API/语法

#include <stdlib.h>

// 内存分配
void *malloc(size_t size);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size);
void free(void *ptr);

#include <string.h>

// 内存操作
void *memcpy(void *dest, const void *src, size_t n);
void *memmove(void *dest, const void *src, size_t n);
void *memset(void *s, int c, size_t n);
int memcmp(const void *s1, const void *s2, size_t n);

21.3 代码示例(完整可编译,附gcc命令)

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

void demonstrate_malloc(void) {
    printf("=== malloc ===\n");

    int *arr = malloc(5 * sizeof(int));
    if (arr == NULL) {
        fprintf(stderr, "malloc failed\n");
        return;
    }

    // 注意:malloc不初始化,内容随机
    printf("Uninitialized values: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    // 手动初始化
    for (int i = 0; i < 5; i++) {
        arr[i] = i * 10;
    }

    printf("After init: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    free(arr);
    arr = NULL;  // 防止野指针
}

void demonstrate_calloc(void) {
    printf("\n=== calloc ===\n");

    int *arr = calloc(5, sizeof(int));
    if (arr == NULL) {
        fprintf(stderr, "calloc failed\n");
        return;
    }

    // calloc自动初始化为0
    printf("Zero-initialized: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    free(arr);
    arr = NULL;
}

void demonstrate_realloc(void) {
    printf("\n=== realloc ===\n");

    int *arr = malloc(3 * sizeof(int));
    if (arr == NULL) return;

    for (int i = 0; i < 3; i++) arr[i] = i;

    printf("Original (3): ");
    for (int i = 0; i < 3; i++) printf("%d ", arr[i]);
    printf("\n");

    // 扩容到6
    int *temp = realloc(arr, 6 * sizeof(int));
    if (temp == NULL) {
        fprintf(stderr, "realloc failed\n");
        free(arr);
        return;
    }
    arr = temp;

    // 新空间可能未初始化
    for (int i = 3; i < 6; i++) arr[i] = i * 10;

    printf("Expanded (6): ");
    for (int i = 0; i < 6; i++) printf("%d ", arr[i]);
    printf("\n");

    free(arr);
    arr = NULL;
}

void demonstrate_mem_functions(void) {
    printf("\n=== 内存操作函数 ===\n");

    // memcpy
    int src[] = {1, 2, 3, 4, 5};
    int dest[5];
    memcpy(dest, src, sizeof(src));
    printf("memcpy: ");
    for (int i = 0; i < 5; i++) printf("%d ", dest[i]);
    printf("\n");

    // memmove(处理重叠)
    int data[] = {1, 2, 3, 4, 5, 6, 7, 8};
    memmove(data + 2, data, 6 * sizeof(int));  // 重叠复制
    printf("memmove: ");
    for (int i = 0; i < 8; i++) printf("%d ", data[i]);
    printf("\n");

    // memset
    char buffer[20];
    memset(buffer, 'A', sizeof(buffer) - 1);
    buffer[sizeof(buffer) - 1] = '\0';
    printf("memset: %s\n", buffer);

    // memcmp
    int a[] = {1, 2, 3};
    int b[] = {1, 2, 4};
    printf("memcmp: %d\n", memcmp(a, b, 3 * sizeof(int)));
}

void demonstrate_memory_leak(void) {
    printf("\n=== 内存泄漏示例 ===\n");

    // 危险:覆盖指针导致泄漏
    int *p = malloc(100 * sizeof(int));
    printf("Allocated at %p\n", (void *)p);

    // 错误:覆盖指针,原内存泄漏
    // p = malloc(200 * sizeof(int));  // 原内存无法释放!

    free(p);
    p = NULL;
    printf("Freed and set to NULL\n");
}

int main() {
    demonstrate_malloc();
    demonstrate_calloc();
    demonstrate_realloc();
    demonstrate_mem_functions();
    demonstrate_memory_leak();

    return 0;
}

编译命令:

gcc -Wall -Wextra -o memory_manage memory_manage.c
./memory_manage

21.4 注意事项与易错点

  1. malloc必须检查返回值:内存不足时返回NULL
  2. free后指针置NULL:防止野指针被误用
  3. realloc失败时原指针有效:不要直接p = realloc(p, size),应先用临时变量接收
  4. 不能重复free:同一指针多次free是未定义行为
  5. free非malloc返回的指针:必须使用malloc/calloc/realloc返回的指针调用free
  6. memset对int数组memset(arr, 0, n)只适用于字节为0的情况

21.5 面试要点(3-5个Q&A)

Q1: malloc和calloc有什么区别? A: malloc不初始化内存,calloc初始化为零。calloc参数是元素数量和大小,malloc是总字节数。calloc可能稍慢。

Q2: realloc扩容时会发生什么? A: 如果原空间后有足够空间,直接扩展;否则分配新空间,复制数据,释放原空间。因此realloc后指针可能改变。

Q3: 如何检测内存泄漏? A: 使用valgrind、AddressSanitizer等工具。良好的习惯是malloc和free配对,free后置NULL,使用智能指针(C++)。

Q4: 什么时候用栈,什么时候用堆? A: 小型、生命周期短的数据用栈(快、自动管理)。大型、动态大小、需要跨函数生命周期的数据用堆。

Q5: memcpy和memmove有什么区别? A: memcpy不处理内存重叠,重叠时行为未定义。memmove处理重叠,更安全但可能稍慢。不确定是否重叠时用memmove。