一动不动是王八?动态内存有话说

简介: 动态内存操作函数的使用

前言

一动不动是王八,出自2014年的春晚,小时候经常喜欢说这句话,那在我们C语言中,我们知道,通常我们开辟一块空间用于存放变量的时候,这块空间是不会改变大小的,但是有的时候,需要我们程序运行起来才会知道我们需要多大的内存空间,那这种情况下,我们只能创建一个很大的数组,用来存放,这个时候就出现了不必要的浪费,那这个时候,就出现了我们的动态内存,让我们的内存不在做“王八”。

动态内存函数介绍

malloc

image.png


malloc,一个用于开辟连续内存空间的函数,它可以像内存申请一块空间,然后返回这块空间起始地址的指针,对于我们的malloc来说,如果这块空间申请失败的话,会返回我们的空指针NULL,那同时我们看到malloc的返回类型是void*,因为我们的malloc是不知道我们申请的空间是给什么类型去使用的,所以我们一般会对申请下来的空间进行强转,我们给malloc传过去的size代表着我们需要申请多少个字节的空间,在C语言标准中,是没有明确规定我们的size等于0会发生什么,所以当size等于0的时候,会发生什么取决于编译器,如果我们想用malloc申请一块20个字节的空间给我们char类型的数组str用,那怎么用呢?


intmain()
{
char*str=NULL;
str=(char*)malloc(20);
if(str==NULL)
    {
perror(str);
    }
return0;
}


那这样就开辟了一块20个字节的空间给我们的str使用。

free

image.png

free,当我们用动态内存函数开辟空间之后,是需要进行内存释放的,不然就会造成内存泄漏,或者其他问题,这个时候,内存释放用的就是我们的free,它可以对我们开辟的空间进行释放,当我们进行释放之后,我们最好让先前接收这片空间的指针置空,以免造成野指针问题,那有了free之后,我们对于上面的例子代码进行升级。


intmain()
{
char*str=0;
str=(char*)malloc(20);
if(str==NULL)
    {
perror(str);
    }
free(str);
str=NULL;
return0;
}


calloc

image.png


calloc,和我们的malloc用处是一样的,但是传参是不一样的,对于我们的malloc来说,可以申请任意字节的空间,比如17、21,那这些字节也可以强转给我们的int类型,只不过我们在往申请的这片空间中放东西的时候,因为我们的int类型是4个字节,所以放入到最后字节不够4个字节的空间时,就会产生截断,放不完全,而我们的calloc传的两个参数,第一个是要申请多少个元素,第二个是每个元素的大小,比如我们要为一个10个元素的int类型的数组申请空间,就可以像下面这样来申请。


intmain()
{
int*num=0;
num=(int*)calloc(10,sizeof(int));
if(num==NULL)
    {
perror(num);
    }
free(num);
num=NULL;
return0;
}


其中我们的calloc和malloc还有一个最大的差别,对于我们的malloc来说,它申请下来的空间是不会对其初始化的,所以malloc内存空间里面放的值是不知道的,但是我们的calloc是会对申请的空间进行初始化的,会全部初始化成0。

realloc

image.png


realloc,是让我们内存“动”起来的关键所在,它的作用是我们发现我们用malloc或者calloc申请的空间不够使用的时候,用来“扩容”的,它的参数有两个,第一个就是我们指向我们申请下来那块空间起始地址的指针,第二个就是“扩容”多少个字节,同样,返回的是申请到空间的起始地址,但是如果我们原空间后面的空间不够“扩容”的字节,就会重新找一片空间,然后把原空间的数据拷贝过去,同样,如果申请失败会返回空指针,所以对于我们要“扩容”的时候,最好是在创建一个指针变量,先用新创建的指针变量接收realloc的返回值,然后判断,申请成功就把realloc申请下来的空间赋值给我们的原指针。


intmain()
{
char*str=NULL;
str=(char*)malloc(20);
if(str==NULL)
    {
perror(str);
    }
char*p=NULL;
p=(char*)realloc(str,10);
if(p!=NULL)
    {
str=p;
p=NULL;
    }
free(str);
str=NULL;
return0;
}


柔性数组

柔性数组,于C99标准中定义,它处于结构体当中,当我们结构体的最后一个成员是一个未知大小的数组的时候,就是我们的柔性数组,当然,这个结构体一定要有其他的成员,可能看字看不懂?那我们直接上代码。


structst_type{
inti;
inta[0];//柔性数组};


这种写法可能在有的编译器上跑不过,那我们就换一种写法。


structst_type{
inti;
inta[];//柔性数组};


柔性数组特点

我们前面提到,柔性数组之前必须至少有一个成员,那是为什么呢?对于柔性数组,如果用sizeof计算它的大小会是多少呢?我们用编译器试一下。

image.png

我们可以看到,整个结构体大小是4,那为什么是4?一个int就占用了四个内存了,何况我们还有一个数组,这里就直观的说明了,对于我们的柔性数组成员,它是不计入结构体大小的,如果这个结构体就它一个成员,那它大小就是0,那不方便我们进行后面的操作。同样对于我们柔性数组来说,它的内存占用也是用我们的malloc来申请,它申请的时候不和我们上面申请的时候是一样的,它需要先将我们结构体内其他成员的空间先跨过,才是自己的。


type_a*p= (type_a*)malloc(sizeof(type_a)+100*sizeof(int));


柔性数组的优点

方便内存释放

当里面的成员不是柔性数组而是指针的时候,在使用的过程中,我们对其内部进行了二次的内存分配,而且把整个结构体的指针返回去了,别人使用这个结构体指针,他并不知道里面进行了二次的内存分配,他直接用free释放了整个结构体,不知道要对内部进行free,但是要是柔性数组,就没有这个问题,free一次就行了。

提高我们的访问速度

因为我们申请下来的空间是一片连续的空间,是更加有利于访问的。

总结

动态内存大大的提高了对内存空间的利用,避免了一些不必要的内存浪费。

相关文章
|
7月前
|
程序员 编译器 C语言
C语言:动态内存(一篇拿捏动态内存!)
C语言:动态内存(一篇拿捏动态内存!)
120 2
|
7月前
|
存储 Linux C语言
【C++练级之路】【Lv.5】动态内存管理(都2023年了,不会有人还不知道new吧?)
【C++练级之路】【Lv.5】动态内存管理(都2023年了,不会有人还不知道new吧?)
|
存储 程序员 编译器
我眼中的‘C’——动态内存+柔型数组
我眼中的‘C’——动态内存+柔型数组
112 0
|
7月前
|
存储 编译器 Linux
【内存管理大猫腻:从“越界”到“内存泄漏”应有尽有】
【内存管理大猫腻:从“越界”到“内存泄漏”应有尽有】
|
编译器 Linux Go
C生万物 | 动态内存管理-2
C生万物 | 动态内存管理
52 0
C生万物 | 动态内存管理-2
|
程序员 C语言 C++
【C语言航路】第十三站:动态内存管理(下)
【C语言航路】第十三站:动态内存管理
48 0
|
编译器 C语言
【C语言航路】第十三站:动态内存管理(上)
【C语言航路】第十三站:动态内存管理
77 0
|
C++
【C++从0到王者】第十一站:引用计数与写时拷贝
【C++从0到王者】第十一站:引用计数与写时拷贝
52 0
|
存储 小程序 C语言
我的创作纪念日———C/C++之动态内存管理
我的创作纪念日———C/C++之动态内存管理
56 0