IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 一篇文章玩转C语言动态内存,轻松管理动态内存空间 -> 正文阅读

[C++知识库]一篇文章玩转C语言动态内存,轻松管理动态内存空间

1、动态内存分配存在的原因

我们普遍定义变量的时候,经常采用以下的写法:

char str;//在栈上开辟1个字节的内存空间
int num;//在栈上开辟4个字节的内存空间
int arr[10];//在栈上开辟40字节连续的空间

但是上述的开辟空间的方式有两个特点:
1、空间开辟大小是固定的。
2、数组在声明的时候,必须指定数组的长度,他所需要的内存在编译时分配。

但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了。这时候就只能试试动态存开辟了。

2、动态内存函数的介绍

  • C语言提供了一些动态内存开辟的函数:使得存储空间的使用更加灵活,提高内存的利用率。
  • malloc和free都声明在 stdlib.h 头文件中。所以在使用的时候要引相应的头文件,下面让我们来看一下这些函数的用法。

2.1malloc函数

1、malloc函数的介绍:这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。

void* malloc(size_t size);

向内存堆区申请size个字节的空间,申请完毕后返回堆区的地址,因为不知道返回什么类型的地址合适,所以返回void*类型的。
2、如果开辟成功,则返回一个指向开辟好空间的指针。

3、如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。

4、返回值的类型是 void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候由使用者自己来决定。一般情况下不拿void*来接收,要强制类型转换后进行使用。

5、如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

6、在使用malloc开辟空间的时候,一定要判断是否开辟成功

int main()
{
    int *p=(int*)malloc(40);
    if(p==NULL)
    {
        return -1;
    }
    int i=0;
    //开辟的空间地址是连续的,可以看成数组
    for(i=0;i<10;i++)
    {
        *(p+i)=i;
    }
   
}

我们申请空间,使用空间,在使用完成后我们要释放空间,此时就用到了free函数。

2.2free函数

C语言提供了另外一个函数free,专门是用来做动态内存的释放和回收的,函数原型如下:
1、free函数的介绍:

void free (void* ptr);

free函数用来释放动态开辟的内存。
2、free的使用:

int main()
{
    int *p=(int*)malloc(40);
    if(p==NULL)
    {
        return -1;
    }
    int i=0;
    //开辟的空间地址是连续的,可以看成数组
    for(i=0;i<10;i++)
    {
        *(p+i)=i;
    }
    free(p);
    p=NULL;
   
}

free把p的空间给释放掉了,但是不会改变p的地址。这种释放后依然能找到free释放的空间是非常可怕的,所以要将释放的空间置空。

3、如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。free也不知所措了。

4、如果参数 ptr 是NULL指针,则函数什么事都不做。所以可以给free传一个空指针,但是free什么事都不会干。

2.3calloc函数

C语言还提供了一个函数叫 calloc , calloc函数也用来动态内存分配。原型如下:

void* calloc (size_t num, size_t size);

1、calloc函数介绍:num表示元素的个数,size表示一个元素有多大,单位字节

#incldue<stdio.h>
#include<errno.h>
#include<string.h>
int main()
{
    int *p=(int*)calloc(10,sizeof(int));
    //开辟10个大小为4字节的连续空间,并且初始化为0
    if(p==NULL)
    {
        printf("%s\n",strerror(errno));
        return -1;
    }
    free(p);
    p=NULL;
    return 0;
}

2、malloc函数只负责在堆区申请空间,并且返回起始地址,不初始化内存空间;calloc函数在堆区上申请空间,并且初始化为0,返回起始地址。

3、如果我们对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数来完成任务。

2.4realloc函数

有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时候内存,我们一定会对内存的大小做灵活的调整。那realloc函数就可以做到对动态开辟内存大小的调整。函数原型如下:

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

1、realloc函数的介绍:ptr是要调整的内存地址,之前可能有malloc或者calloc开辟好的空间;size 为调整之后新大小,单位字节。

2、返回值为调整之后的内存起始位置。

3、realloc函数的出现让动态内存管理更加灵活。

4、这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到 新 的空间。

5、realloc在调整内存空间的是存在两种情况:

1)情况1:原有空间之后有足够大的空间,要扩展内存就直接在原有内存之后直接追加空间,原来空间的数据不发生变化。把原先空间的地址当作新地址返回。

2)情况2:原有空间之后没有足够大的空间,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用。并将原空间的数据拷贝在新空间上,在释放掉原空间,这样函数返回的是一个新的内存地址。

6、 由于上述的两种情况,realloc开辟失败会返回空指针,如果贸然使用,开辟失败可能会丢失原有空间,所以realloc函数的使用就要注意进行判断,在进行自身的赋值。 举个例子:

#incldue<stdio.h>
#include<errno.h>
#include<string.h>
int main()
{
    int *p=(int*)calloc(10,sizeof(int));
    if(p==NULL)
    {
        printf("%s\n",strerror(errno));
        return -1;
    }
    int* ptr=(int*)realloc(p,20*sizeof(int));
    if(ptr!=NULL)
    {
    p=ptr;
    }
    free(p);
    p=NULL;
    return 0;
}

3、常见的动态内存错误

3.1对NULL空指针的解引用操作

int main()
{
    int *p=(int*) malloc(20);
    *p=0;//这样不进行判断,直接解引用是有风险的。
    if(p==NULL)
    {
        return -1;
    }
}

结论:所以对动态内存开辟的空间一定要先判断在使用。如下:

int main()
{
    int *p=(int*) malloc(20);
    if(p==NULL)
    {
        return -1;
    }
    *p=0;
}

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

int main()
{
    int *p=(int*)malloc(200);
    if(p==NULL)
    {
        return -1;
    }
    int i=0;
    //200字节也就是50个左右的整型,访问80个越界访问了,程序会直接崩掉
    for(i=0;i<80;i++)
    {
        *(p+i)=i;
    }
    free(p);
    p=NULL:
    return 0;
}

结论:在对动态开辟的内存使用时,一定要注意范围。

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

int main()
{
    int a=10;
    int* p=&a;
    free(p);
    //处在栈上的空间直接free,即非堆上的空间释放程序会直接崩掉。
    p=NULL;
    return 0;
}

结论:对非堆上的内存不可以使用free释放。

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

int main()
{
    int* p=(int*)malloc(10*sizeof(int));
    if(p==NULL)
    {
        return -1;
    }
    int i=0;
    for(i=0;i<10;i++)
    {
        *p++=i;
//p在++的过程中,p已经不再执行开辟空间的起始位置,free掉的只是现在位置往后的空间。    
  
    }
    free(p);
    p=NULL;
    return 0;
}

结论:++/–在某些情况下是具有副作用的,对于地址的起始位置是非常重要的,如果要用到地址的偏移,可以先保存原先地址的起始位置。即给原地址进行一个备份。

3.5对同一块内存的多次释放

int main()
{
    int* p=(int*)malloc(40);
    if(p==NULL)
    {
        return -1;
    }
    free(p);
    free(p);
    //程序直接崩溃
    return 0;
}

另一种错误:

int main()
{
    int* p=(int*)malloc(40);
    if(p==NULL)
    {
        return -1;
    }
    free(p);
    p=NULL;
    free(p);
    //此时程序可以正常运行
    return 0;
}

结论:对释放的空间一定要进行及时的置空,并且不能重复释放同一空间。即使有两个指向同一内存的,也只能free一个,因为释放完之后,原有的内存空间已经释放掉了,在进行释放就是重复释放。

4、后续有动态内存的面试题及其运用,收藏+关注不迷路哟。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-27 13:54:18  更:2021-09-27 13:54:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 22:11:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码