文章目录

C 语言动态内存:realloc() 函数的扩容机制

发布于 2026-04-03 22:43:15 · 浏览 2 次 · 评论 0 条

C 语言动态内存:realloc() 函数的扩容机制

在 C 语言中,当你使用 malloc()calloc() 分配了一块堆内存后,有时会发现空间不够用。此时,不要手动复制数据并重新分配内存,而是应优先考虑使用 realloc() 函数——它能自动完成“扩容+数据迁移”的全过程。


realloc() 的基本用法

realloc() 的函数原型如下:

#include <stdlib.h>
void *realloc(void *ptr, size_t new_size);
  • 第一个参数 ptr 是你之前通过 malloc()calloc()realloc() 分配的内存指针。
  • 第二个参数 new_size 是你希望的新内存大小(以字节为单位)。
  • 返回值是一个指向新内存块的指针,类型为 void*,通常需要强制转换为你实际使用的类型。

调用 realloc(ptr, new_size) 后,系统会尝试将原内存块扩展到 new_size 字节。如果成功,原数据会被完整保留,新增部分内容未定义;如果失败,返回 NULL,但原内存块保持不变,不会被释放。


扩容的三种可能情况

realloc() 在内部根据系统内存布局和请求大小,有三种处理方式:

  1. 原地扩容:如果原内存块后面有足够的连续空闲空间,系统直接扩展该块,不移动数据,返回原指针。
  2. 异地扩容:如果原位置无法扩展,系统会在堆中另找一块足够大的新内存,自动复制原数据过去,然后释放旧内存,返回新地址。
  3. 扩容失败:如果系统无法找到足够内存,返回 NULL原内存仍然有效,需手动处理错误。

因此,永远不要直接写 ptr = realloc(ptr, new_size);。一旦失败,ptr 会被赋值为 NULL,导致原内存地址丢失,造成内存泄漏。

正确做法是使用临时指针接收返回值:

int *temp = realloc(ptr, new_size);
if (temp == NULL) {
    // 处理错误:原 ptr 仍有效
    fprintf(stderr, "realloc failed\n");
    // 可选择继续使用原内存,或退出
} else {
    ptr = temp; // 只有成功才更新指针
}

实际操作步骤:安全使用 realloc()

  1. 确保原指针合法ptr 必须是由 malloc()calloc()realloc() 返回的有效指针,或者为 NULL(此时 realloc(NULL, size) 等价于 malloc(size))。
  2. 计算新大小:明确你需要多少字节。例如,若要将整型数组从 n 个元素扩容到 m 个,则新大小为 m * sizeof(int)
  3. 用临时指针调用 realloc()
    int *new_ptr = realloc(old_ptr, m * sizeof(int));
  4. 检查返回值是否为 NULL
    • 如果是 NULL不要释放 old_ptr,也不要继续使用新内存。
    • 如果不是 NULL,将 old_ptr 更新为 new_ptr
  5. 继续使用新内存:此时 ptr 指向的内存至少包含原数据,可安全访问前 min(原大小, 新大小) 字节。
  6. 最终记得 free(ptr):无论是否扩容成功,只要内存不再需要,就应调用 free() 释放。

常见误区与注意事项

问题 正确做法
直接赋值 ptr = realloc(ptr, size); 使用临时指针判断是否成功后再赋值
对栈变量或全局变量指针调用 realloc() 只能对堆内存(malloc 系列分配的)使用
忽略返回值类型转换(C++ 中) C 语言可隐式转换,但为清晰建议显式转,如 (int*)realloc(...)
认为新分配的额外内存会被清零 realloc() 不会初始化新增部分,如需清零,应配合 memset() 或改用 calloc() 初始分配

扩容性能与内存碎片

realloc() 的效率取决于是否发生“异地迁移”。频繁的小幅扩容(如每次只增加几个字节)容易导致:

  • 多次内存复制,降低性能;
  • 产生内存碎片,使后续大块分配失败。

建议策略:采用“指数增长”方式扩容。例如,初始分配 4 个元素,不够时扩为 8,再不够扩为 16……这样均摊下来,每次插入的平均成本是常数级别(即“摊还分析”中的 O(1))。

示例代码:

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

int main() {
    size_t capacity = 4;
    size_t size = 0;
    int *arr = malloc(capacity * sizeof(int));
    if (!arr) return 1;

    for (int i = 0; i < 20; i++) {
        if (size >= capacity) {
            capacity *= 2; // 指数扩容
            int *temp = realloc(arr, capacity * sizeof(int));
            if (!temp) {
                fprintf(stderr, "Out of memory\n");
                free(arr);
                return 1;
            }
            arr = temp;
        }
        arr[size++] = i;
    }

    for (size_t i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    free(arr);
    return 0;
}

此程序动态维护一个整数数组,自动扩容,避免了频繁重分配。


特殊情况:缩小内存

realloc() 也可用于缩小已分配内存。例如:

ptr = realloc(ptr, smaller_size);

此时,系统可能:

  • 直接截断多余部分(原地操作);
  • 或者什么都不做(因为释放少量尾部内存收益低)。

但无论如何,smaller_size 字节的数据一定保留,超出部分不可访问。


总结关键原则

  • 永远用临时指针接收 realloc() 返回值
  • 仅对堆内存使用 realloc(),不可用于栈或静态变量。
  • 新增内存未初始化,勿直接读取。
  • 失败时不释放原内存,需自行处理错误。
  • 合理设计扩容策略,避免线性增长导致性能下降。
// 安全模板
void *safe_realloc(void *ptr, size_t new_size) {
    void *temp = realloc(ptr, new_size);
    if (temp == NULL && new_size > 0) {
        // 分配失败且不是释放操作
        // 此处可根据需求 abort() 或记录错误
        return NULL;
    }
    return temp;
}

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文