C语言动态分配内存(malloc,calloc,realloc,free)

一.为什么需要动态内存分配

1.内存的五大分区

(1)堆区(heap)——由程序员分配和释放, 若程序员不释放,程序结束时一般由操作系统回收。注意它与数据结构中的堆是两回事

(2)栈区(stack)——由编译器自动分配释放 ,存放函数的参数值,局部变量等。其操作方式类似于数据结构中的栈

(3)静态全局区

         1)未初始化静态全局区 —— 静态变量,全局变量,没有初始化的存在此区

         2)初始化的静态全局区 —— 静态变量、全局变量,赋过初值的存放在此区

(4)文字常量区——常量、字符串就是放在这里的。 程序结束后由系统释放

(5)(程序)代码区——用于存放函数体的(二进制)代码

2.动态内存优势

我们通过声明变量来为其分配内存空间,这种方式称为静态内存分配。然而,静态内存分配的缺点是,内存空间的大小在编译时必须确定,而且无法根据程序的运行时需求进行动态调整。

    动态内存允许程序在运行时根据需要动态地分配和释放内存空间。动态内存分配对于处理未知大小的数据,以及需要在运行时生成数据结构的情况非常有用。使用动态内存分配,程序可以更加灵活地管理内存,提高内存的利用率。

二.动态内存分配的函数

1. malloc()

用于分配指定大小的内存块。其函数原型如下:

void *malloc(size_t size);

malloc() 接受一个参数 size,表示需要分配的字节数。如果内存分配成功,它返回一个指向已分配内存区域的指针;否则,若无法分配足够的内存,返回 NULL。

2. calloc()

与 malloc() 类似,但额外提供了分配内存并将其所有字节初始化为零的功能。其函数原型如下:

void *calloc(size_t num, size_t size);

calloc() 接收两个参数:num 表示元素数量,size 表示每个元素的大小。它为 num 个大小为 size 的元素分配内存,并清零。返回值与 malloc() 相同。

3. realloc()

用于调整已分配内存块的大小。其函数原型如下:

void *realloc(void *ptr, size_t size);

realloc() 接收两个参数:ptr 是先前通过 malloc()、calloc() 或 realloc() 分配的内存区域的指针,size 是新的所需大小。它尝试调整指定内存块的大小,如果必要,可能会移动内存块到另一个位置。返回值为调整后内存块的新地址(可能与原地址相同或不同),若分配失败,则返回 NULL,此时原始内存块保持不变。

4. free()

用于释放之前动态分配的内存。其函数原型如下:

void free(void *ptr);

free() 接收一个参数 ptr,即之前由内存分配函数返回并不再使用的内存区域的指针。调用 free() 后,该内存区域被释放,可供后续分配使用。

三.动态内存分配函数的实例

1. malloc() 示例

#include <stdio.h>

#include <stdlib.h>

int main() {

    // 分配一个能存储10个整数的空间

    int *dynamicArray = (int*)malloc(sizeof(int) * 10);

    if (dynamicArray == NULL) {

        printf("Memory allocation failed.\n");

        return 1;

    }

    // 使用分配的内存

    for (int i = 0; i < 10; ++i) {

        dynamicArray[i] = i * i;

    }

    // 输出动态数组的内容

    for (int i = 0; i < 10; ++i) {

        printf("Element at index %d: %d\n", i, dynamicArray[i]);

    }

    // 释放内存

    free(dynamicArray);

    dynamicArray = NULL;

    return 0;

}

在这个例子中,我们首先使用 malloc() 分配了一个能存储10个整数的空间。然后,我们填充这个动态数组,并打印其内容。最后,我们调用 free() 释放内存,并将指针置为 NULL,防止后续误用。

2. calloc() 示例

#include <stdio.h>

#include <stdlib.h>

int main() {

    // 分配并初始化一个能存储5个浮点数的空间

    float *initializedArray = (float*)calloc(5, sizeof(float));

    if (initializedArray == NULL) {

        printf("Memory allocation failed.\n");

        return 1;

    }

    // 使用分配的内存

    for (int i = 0; i < 5; ++i) {

        initializedArray[i] = 1.0f / (i + 1);

    }

    // 输出初始化后的动态数组

    for (int i = 0; i < 5; ++i) {

        printf("Element at index %d: %.2f\n", i, initializedArray[i]);

    }

    // 释放内存

    free(initializedArray);

    initializedArray = NULL;

    return 0;

}

此例中,我们使用 calloc() 分配并初始化了一个能存储5个浮点数的空间。所有元素初始值为0.0。然后,我们填充这个动态数组并打印其内容。最后,我们释放内存并将指针置为 NULL。

3. realloc() 示例

#include <stdio.h>

#include <stdlib.h>

int main() {

    int *dynamicArray = (int*)malloc(sizeof(int) * 5);

    if (dynamicArray == NULL) {

        printf("Memory allocation failed.\n");

        return 1;

    }

    // 填充初始的动态数组

    for (int i = 0; i < 5; ++i) {

        dynamicArray[i] = i * i;

    }

    // 打印初始数组内容

    printf("Initial array:\n");

    for (int i = 0; i < 5; ++i) {

        printf("Element at index %d: %d\n", i, dynamicArray[i]);

    }

    // 尝试将数组容量扩展至10个元素

    int *temp = (int*)realloc(dynamicArray, sizeof(int) * 10);

    if (temp != NULL) {

        dynamicArray = temp;

        // 填充新增的元素

        for (int i = 5; i < 10; ++i) {

            dynamicArray[i] = i * i;

        }

    } else {

        printf("Failed to reallocate memory. Keeping original size.\n");

    }

    // 打印扩展后的数组内容(或保持原大小)

    printf("Reallocated array:\n");

    for (int i = 0; i < (temp == NULL ? 5 : 10); ++i) {

        printf("Element at index %d: %d\n", i, dynamicArray[i]);

    }

    // 释放内存

    free(dynamicArray);

    dynamicArray = NULL;

    return 0;

}

在这个例子中,我们首先使用 malloc() 分配了一个能存储5个整数的空间,并填充了初始值。然后,我们尝试使用 realloc() 将数组容量扩展至10个元素。如果成功,我们填充新增的元素;否则,保持原大小。

最后,我们打印扩展后的数组内容(或保持原大小),并释放内存。

四.注意事项

1.动态分配内存注意

检查返回值:在使用 malloc()、calloc() 和 realloc() 后,应检查返回的指针是否为 NULL,以判断分配是否成功。

避免内存泄漏:当不再需要动态分配的内存时,务必调用 free() 进行释放。忘记释放会导致内存泄漏,长期运行的程序可能因此耗尽系统资源。

匹配类型:分配内存时确保计算的大小与要存储的数据类型相符。使用 sizeof 运算符可以确保正确计算所需字节数。

不要对未分配的内存或已释放的内存进行操作:这会导致未定义行为,严重时程序崩溃。

使用 realloc() 时保护原有数据:如果 realloc() 返回新的地址,记得更新指向内存区域的所有指针,因为原有的内存可能已被移动。同时,如果 realloc() 失败,原内存块保持不变,仍可继续使用。

避免内存碎片:合理规划内存分配与释放,减少频繁的小块内存分配与释放,有助于降低内存碎片,提高内存利用率。

遵循分配与释放对称原则:确保每个 malloc()、calloc() 或 realloc() 都有对应的 free() 调用,且释放的是同一指针。

2.free(NULL)的问题

  在C语言中free(NULL)的操作是合法的,C语言标准规定:如果free的参数是NULL,那么这个函数就什么也不做。

3.malloc(0)的问题

  在C语言中malloc(0)的语法也是对的,而且确实也分配了内存,但是内存空间是0,这个看起来说法很奇怪,但是从操作系统的原理来解释就不奇怪了。

  在内存管理中,内存中有栈和堆两个部分,栈有自己的机器指令,是一种先进后出的数据结构。而malloc分配的内存是堆内存,由于堆没有自己的机器指令,所以要由自己编写算法来管理这片内存,通常的做法是用链表在每片被分配的内存前加个表头,里面存储了被分配内存的起始地址和大小。malloc等函数返回的就是表头里的起始指针(这个地址是由一系列的算法得来的,而这些操作又是由编译器的底层为我们做的,我们并不需要关心如何操作)

  动态分配内存成功之后,就会返回一个有效的指针。而对于分配0空间来说,算法会得出一个可用内存的起始地址,但可用的空间为0,而操作系统一般不知道其终止地址,一般是根据占用大小来推出终止地址的。所以对malloc(0)返回的指针进行操作就是错误的。

  但需要注意,即使malloc(0)也要记得free掉,因为malloc还会额外分配内存来维护申请的空间,malloc(0)时并不是什么也不做。

  • 15
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI+程序员在路上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值