C进阶 | 动态内存管理

简介: 动态内存管理(动态内存分配),就是指在程序执行的过程中动态地分配或者回收存储空间的方法。

在这里插入图片描述
啊我摔倒了..有没有人扶我起来学习....


@TOC


前言

动态内存管理(动态内存分配),就是指在程序执行的过程中动态地分配或者回收存储空间的方法。


一、为什么要有动态内存管理?

  • C/C++中,编写程序有时不能确定数组应该定义为多大,因此在程序运行时要根据需要从系统中动态地获得内存空间
  • 动态内存管理不像数组等静态内存管理方法那样需要预先分配存储空间(这样会限定死空间大小),而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小

二、动态内存管理函数

1. 认识动态内存管理函数

  • 动态内存管理是在堆区上进行内存空间的开辟,开辟到的是一块连续的空间

动态内存管理函数:

#include <stdlib.h> //头文件

void* malloc (size_t size); //size_t表示无符号整形:size是要申请的字节大小
void* calloc (size_t num, size_t size); //num是元素个数,size是每个元素的大小
void* realloc (void* ptr, size_t size); //ptr指向要再次分配内存的地方
void free (void* ptr); //ptr指向经动态内存管理过的地方

2. 动态内存管理函数的使用

  • 以上认识梳理了函数形参之后,我们先来了解一下malloc的使用
  1. 申请
    在这里插入图片描述
    开辟空间是可能失败的,一旦失败malloc就会返回空值NULL,所以使用这个空间前我们需要判定一下p是否为空值,若为空值则不再往下执行程序
    在这里插入图片描述
  2. 使用
    在这里插入图片描述
  3. 释放
    在这里插入图片描述

free括号里直接放上p,这就是为啥使用的时候尽量不要改变p的指向;释放空间之后,相当于把空间还给了系统,这时就要给p赋上空值,避免成为野指针

  1. 初始化情况
    在这里插入图片描述

可以看出malloc在开辟成功后并不会对该空间进行初始化,得我们自己后续赋值

附上整体代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main()
{
    int* p = (int*)malloc(10*sizeof(int));
    if (p == NULL)
    {
        printf("%s\n", strerror(errno));
        return 1;
    }
    //使用
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        *(p+i) = i;
    }
    //释放
    free(p);
    p = NULL;

    return 0;
}

加上#define _CRT_SECURE_NO_WARNINGS 1是因为某些编译器会提示strerror不安全,加上这个就忽略这个提示,但一定要注意,必须加在首行!

  • 至于calloc的使用我就不赘述了,形参注意一下区别就行啦,

你可以认为calloc = malloc + memset

  • realloc是在已经开辟了内存空间的基础上,继续进行开辟空间的操作
  1. 老规矩,先开辟内存空间,这里我继续用malloc申请40字节内存
    在这里插入图片描述
  2. 扩容

在这里插入图片描述
==这里需要注意:==我们不能直接用p接收realloc返回的地址,万一是空值呢?本来p还指向前面所开辟的空间,结果给弄没了。
所以我们先进行判断,不是空值的话再重新赋给p,而ptr起到临时的作用,使用不上了就给它赋空值

  1. 同样地,使用完后要释放
    在这里插入图片描述
  2. 小细节

    • 此时realloc的用法相当于malloc

在这里插入图片描述

- 就算`realloc`开辟成功,返回的地址可能也不是原来p的地址了
(1)此时`malloc`申请的空间后面==还有==位置可以开辟,故返回的地址==还是== p原来所指向的地址
    ![在这里插入图片描述](https://ucc.alicdn.com/images/user-upload-01/2fdeb5956a744017b1c00d363c438d94.png)

    (2)此时`malloc`申请的空间后面==没有==位置可以开辟,故返回的地址==不是== p原来所指向的地址,它会重新寻找一个能容纳80字节的空间,然后把原空间的内容拷贝过来,再自动把原空间释放掉

在这里插入图片描述

附上完整代码
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = (int*)malloc(40);
    if (p == NULL)
        return 1;

    //使用
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        *(p + i) = i;
    }

    //增加空间
    int* ptr = (int*)realloc(p, 80);
    //当realloc开辟失败的是,返回的是NULL
    if (ptr != NULL)
    {
        p = ptr;
        ptr = NULL;
    }

    //释放
    free(p);
    p = NULL;

    return 0;
}

三、常见错误

  • 一般来说,malloc等在使用时,与free配套使用就不会出错,但还是有需要注意的地方

1. 对NULL的指针解应用操作

  • 此时若开辟空间失败,就会返回空值,对空值进行解应用

在这里插入图片描述

2. 对动态开辟空间的越界访问

  • 铁汁们敲多了代码之后往往会昏头,忘了p是整形,+1就跳过4个字节,i < 40的话造成严重的越界访问

在这里插入图片描述

3. 对非动态开辟内存使用free释放

  • 这也属于昏头了,这时会报出严重的错误,该exe执行文件直接卡死

在这里插入图片描述
在这里插入图片描述

4. 使用free释放一块动态开辟内存的一部分

  • 此时也会造成严重错误

在这里插入图片描述
在这里插入图片描述

  • 所以我们前面进行malloc开辟空间的时候才强调不要改变p指向的

在这里插入图片描述

5. 对同一块动态内存多次释放

  • 以下也属于昏头了...要多细心

在这里插入图片描述
在这里插入图片描述

  • 但如果你在每次free完之后及时把p置为空值,即使再次free也不会报错,因为free不会对空值进行任何操作

在这里插入图片描述

6. 动态开辟内存忘记释放(内存泄露)

  • 第一种是你自己忘记用完之后释放了,会导致这部分空间一直被占用,循环往复最后造成内存泄露。
  • 第二种是比如别人写了一个函数,开辟了动态内存,但是需要你用完这个函数的时候来释放,但你却忘了,也会造成内存泄露。假如是你写这样的函数,要备注让别人知道你开辟了动态内存

注:当某一天你运行某个程序,发现内存空间消耗得很快乃至耗光,可能导致机器直接卡死,重启之后又没事了,但是一旦运行又是一样的问题,那就要考虑内存泄漏的问题,内存泄露就像慢性咽炎,啊啊啊


在这里插入图片描述

相关文章
|
2月前
|
安全 程序员 编译器
动态内存管理学习分享
动态内存管理学习分享
52 0
|
2月前
|
编译器 程序员 C语言
C语言从入门到实战——动态内存管理
在C语言中,动态内存管理是指程序运行时,通过调用特定的函数动态地分配和释放内存空间。动态内存管理允许程序在运行时根据实际需要来分配内存,避免了静态内存分配在编译时就确定固定大小的限制。
49 0
|
8月前
|
C语言
关于动态内存管理中的常见练习题
关于动态内存管理中的常见练习题
49 0
|
2月前
|
程序员 编译器 C语言
【C语言进阶】动态内存管理
【C语言进阶】动态内存管理
|
8月前
|
安全 编译器
【C进阶】动态内存管理
【C进阶】动态内存管理
32 0
【C进阶】动态内存管理
|
8月前
C进阶-动态内存管理+柔性数组(1)
C进阶-动态内存管理+柔性数组
27 0
|
8月前
|
编译器 C语言 C++
C进阶-动态内存管理+柔性数组(2)
C进阶-动态内存管理+柔性数组
39 0
|
10月前
|
存储 编译器 C语言
【C++初阶】动态内存管理
【C++初阶】动态内存管理
104 0
|
程序员 C语言 C++
动态内存管理【下篇】
动态内存管理【下篇】
71 0
|
编译器 C语言
动态内存管理【上篇】
动态内存管理【上篇】
68 0