malloc与realloc有何区别?

99ANYc3cd6
预计阅读时长 19 分钟
位置: 首页 C语言 正文

核心概念:为什么需要动态内存分配?

在 C 语言中,内存分为几个区域:

c语言 malloc realloc
(图片来源网络,侵删)
  1. 栈区:存储局部变量、函数参数等,由编译器自动管理,速度快,但大小有限,函数返回时,其栈上的变量会被销毁。
  2. 静态/全局区:存储全局变量和 static 变量,程序启动时分配,结束时释放。
  3. 常量区:存储字符串字面量等常量。
  4. 堆区:这是动态内存分配的区域,由程序员手动管理,堆的大小通常比栈大得多,生命周期由程序员控制,直到被 free 释放。

什么时候需要动态内存?

  • 当你需要的内存大小在编译时无法确定(从用户输入获取数组大小)。
  • 当你需要的数据量非常大,超过了栈的容量限制。
  • 当你需要的数据需要在函数之间共享,而函数返回后栈上的数据会失效。

malloc (Memory Allocate)

malloc 的作用是在上分配一块指定大小的连续内存空间。

函数原型

#include <stdlib.h> // 必须包含这个头文件
void *malloc(size_t size);

参数

  • size_t size:你想要分配的内存字节数。

返回值

  • 成功时:返回一个指向分配好的内存块起始地址void* 指针。
  • 失败时:如果堆上没有足够的连续内存空间,malloc 会返回 NULL 指针。

关键特点

  1. 内存是未初始化的malloc 分配的内存中是“垃圾数据”,你必须在使用前手动进行初始化(用 memset 或循环赋值)。
  2. *返回 `voidmalloc` 返回的是一个通用指针,你需要根据你的数据类型进行强制类型转换。
  3. 只分配,不初始化:它只负责在堆上找一块空间给你,这块空间里是什么内容它不关心。

使用示例

#include <stdio.h>
#include <stdlib.h>
int main() {
    int n;
    printf("请输入要存储的整数个数: ");
    scanf("%d", &n);
    // 1. 检查输入是否合法
    if (n <= 0) {
        printf("输入无效,\n");
        return 1;
    }
    // 2. 使用 malloc 分配内存
    // sizeof(int) * n 计算需要的总字节数
    // (int*) 将 void* 强制转换为 int*,因为我们要存储整数
    int *ptr = (int*)malloc(n * sizeof(int));
    // 3. **至关重要的一步:检查分配是否成功**
    if (ptr == NULL) {
        printf("内存分配失败!\n");
        return 1; // 分配失败,程序退出
    }
    // 4. 使用分配的内存(从键盘读入数据)
    printf("请输入 %d 个整数:\n", n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &ptr[i]);
    }
    // 5. 打印内存中的数据
    printf("你输入的整数是:\n");
    for (int i = 0; i < n; i++) {
        printf("%d ", ptr[i]);
    }
    printf("\n");
    // 6. **使用完毕后,必须释放内存!**
    free(ptr);
    // 7. 将指针置为 NULL,这是一个好习惯,防止“野指针”
    ptr = NULL;
    return 0;
}

realloc (Re Allocate)

realloc 的作用是重新调整一块已经分配好的内存的大小,你可以用它来扩大缩小内存块。

函数原型

#include <stdlib.h>
void *realloc(void *ptr, size_t new_size);

参数

  • void *ptr:指向之前由 malloc, calloc, 或 realloc 分配的内存块的指针,如果你想分配一个全新的内存块,可以传入 NULLrealloc 的行为和 malloc 一样。
  • size_t new_size:你希望调整到的的总字节数。

返回值

  • 成功时:返回一个指向调整后的内存块起始地址的 void* 指针。
  • 失败时:如果无法满足 new_size 的要求,返回 NULL

realloc 的工作原理(非常重要!)

realloc 的行为比 malloc 复杂,因为它取决于是否有足够的连续空间:

c语言 malloc realloc
(图片来源网络,侵删)
  1. 如果原地址后面有足够的连续空间

    • realloc 会在原地扩展内存块,并将原有数据复制到新扩展的部分。
    • 返回的指针和原来的 ptr 相同
  2. 如果原地址后面没有足够的连续空间

    • realloc 会在堆的其他地方寻找一块大小为 new_size 的新内存。
    • 找到后,它会将原来内存块中的所有数据复制到新内存中。
    • 它会释放掉原来的内存块
    • 返回的是新内存块的地址,这个地址和原来的 ptr 不同
  3. new_size 比原来小

    • realloc 会截断内存块,只保留前面的 new_size 字节。
    • 返回的指针和原来的 ptr 通常相同

使用示例

#include <stdio.h>
#include <stdlib.h>
int main() {
    int initial_size = 5;
    int *numbers = (int*)malloc(initial_size * sizeof(int));
    if (numbers == NULL) {
        printf("初始内存分配失败!\n");
        return 1;
    }
    // 初始化并打印初始数组
    printf("初始数组 (%d 个元素):\n", initial_size);
    for (int i = 0; i < initial_size; i++) {
        numbers[i] = i * 10;
        printf("%d ", numbers[i]);
    }
    printf("\n\n");
    // 假设我们需要更多的空间
    int new_size = 10;
    int *temp_ptr = (int*)realloc(numbers, new_size * sizeof(int));
    // **检查 realloc 是否成功**
    if (temp_ptr == NULL) {
        printf("内存重新分配失败!\n");
        // 原有的 numbers 指针仍然有效,不能 free 它
        // 我们只能继续使用原来的大小
        printf("继续使用原来的数组,\n");
    } else {
        // realloc 成功,更新指针
        // 注意:temp_ptr 可能是新的地址,也可能是旧的地址
        numbers = temp_ptr;
        printf("数组已成功扩展到 %d 个元素:\n", new_size);
        for (int i = 0; i < new_size; i++) {
            printf("%d ", numbers[i]); // 原有数据仍然存在,新位置是未初始化的垃圾数据
        }
        printf("\n");
    }
    // ... 使用扩展后的数组 ...
    // 释放内存
    free(numbers);
    numbers = NULL;
    return 0;
}

malloc vs realloc 对比总结

特性 malloc realloc
功能 分配一块全新的内存 调整已存在的内存块大小
参数 size_t size (所需字节数) void *ptr (原指针), size_t new_size (新字节数)
初始数据 内存是未初始化 保留原有数据,新扩展部分是未初始化
失败处理 返回 NULL,原指针无效 返回 NULL原指针仍然有效,数据未丢失
典型用法 int *p = malloc(n * sizeof(int)); p = realloc(p, new_n * sizeof(int));

最佳实践和常见错误

  1. 总是检查返回值mallocrealloc 都可能失败,必须检查它们返回的指针是否为 NULL
  2. realloc 失败时不要丢弃原指针:如上所述,realloc 失败时返回 NULL,但你的旧内存块和其中的数据都还在,如果你直接 free 了旧指针,就会造成内存泄漏。
    // 错误示范
    ptr = realloc(ptr, new_size);
    if (ptr == NULL) {
        free(ptr); // 错误!ptr 是 NULL,但原来的内存块已经找不到了
    }
    // 正确示范
    int *new_ptr = realloc(ptr, new_size);
    if (new_ptr == NULL) {
        // 分配失败,但 ptr 仍然指向有效的旧内存
        printf("分配失败,但旧数据还在,\n");
        // 可以选择继续使用 ptr
    } else {
        // 分配成功,更新指针
        ptr = new_ptr;
    }
  3. free 之后将指针置为 NULL:这可以防止“悬垂指针”(Dangling Pointer),即一个指向已释放内存的指针,通过检查 if (ptr != NULL) 可以避免对已释放内存的误操作。
  4. freemalloc/calloc/realloc 过的指针:不要释放未分配的内存或栈上的变量地址。
  5. 不要对 NULL 指针调用 realloc:虽然 realloc 的第一个参数允许是 NULL(此时行为等同于 malloc),但如果你已经将一个指针 free 并置为 NULL,再对它调用 realloc 是不安全的,最好用 malloc 来处理全新内存的分配。
  6. 计算字节时使用 sizeof:始终使用 sizeof(type) 来计算单个元素的大小,而不是硬编码数字(如 4 代表 int 的大小),这样代码更具可移植性。

掌握了 mallocrealloc,你就能编写出更灵活、更强大的 C 语言程序,能够处理动态变化的数据结构,如动态数组、链表等。

c语言 malloc realloc
(图片来源网络,侵删)
-- 展开阅读全文 --
头像
如何解决dede login.php登录报错问题?
« 上一篇 2025-12-01
C语言stat函数如何防止栈溢出?
下一篇 » 2025-12-01

相关文章

取消
微信二维码
支付宝二维码

目录[+]