文章目录
- 前言
- 动态内存函数介绍
- malloc
- free
- calloc
- realloc
- 柔性数组
- 柔性数组特点
- 柔性数组的优点
- 方便内存释放
- 提高我们的访问速度
- 总结
前言
一动不动是王八,出自2014年的春晚,小时候经常喜欢说这句话,那在我们C语言中,我们知道,通常我们开辟一块空间用于存放变量的时候,这块空间是不会改变大小的,但是有的时候,需要我们程序运行起来才会知道我们需要多大的内存空间,那这种情况下,我们只能创建一个很大的数组,用来存放,这个时候就出现了不必要的浪费,那这个时候,就出现了我们的动态内存,让我们的内存不在做“王八”。
动态内存函数介绍
malloc
malloc,一个用于开辟连续内存空间的函数,它可以像内存申请一块空间,然后返回这块空间起始地址的指针,对于我们的malloc来说,如果这块空间申请失败的话,会返回我们的空指针NULL,那同时我们看到malloc的返回类型是void*,因为我们的malloc是不知道我们申请的空间是给什么类型去使用的,所以我们一般会对申请下来的空间进行强转,我们给malloc传过去的size代表着我们需要申请多少个字节的空间,在C语言标准中,是没有明确规定我们的size等于0会发生什么,所以当size等于0的时候,会发生什么取决于编译器,如果我们想用malloc申请一块20个字节的空间给我们char类型的数组str用,那怎么用呢?
int main()
{
char* str=NULL;
str=(char*)malloc(20);
if(str==NULL)
{
perror(str);
}
return 0;
}
那这样就开辟了一块20个字节的空间给我们的str使用。
free
free,当我们用动态内存函数开辟空间之后,是需要进行内存释放的,不然就会造成内存泄漏,或者其他问题,这个时候,内存释放用的就是我们的free,它可以对我们开辟的空间进行释放,当我们进行释放之后,我们最好让先前接收这片空间的指针置空,以免造成野指针问题,那有了free之后,我们对于上面的例子代码进行升级。
int main()
{
char* str=0;
str=(char*)malloc(20);
if(str==NULL)
{
perror(str);
}
free(str);
str=NULL;
return 0;
}
calloc
calloc,和我们的malloc用处是一样的,但是传参是不一样的,对于我们的malloc来说,可以申请任意字节的空间,比如17、21,那这些字节也可以强转给我们的int类型,只不过我们在往申请的这片空间中放东西的时候,因为我们的int类型是4个字节,所以放入到最后字节不够4个字节的空间时,就会产生截断,放不完全,而我们的calloc传的两个参数,第一个是要申请多少个元素,第二个是每个元素的大小,比如我们要为一个10个元素的int类型的数组申请空间,就可以像下面这样来申请。
int main()
{
int* num=0;
num=(int*)calloc(10,sizeof(int));
if(num==NULL)
{
perror(num);
}
free(num);
num=NULL;
return 0;
}
其中我们的calloc和malloc还有一个最大的差别,对于我们的malloc来说,它申请下来的空间是不会对其初始化的,所以malloc内存空间里面放的值是不知道的,但是我们的calloc是会对申请的空间进行初始化的,会全部初始化成0。
realloc
realloc,是让我们内存“动”起来的关键所在,它的作用是我们发现我们用malloc或者calloc申请的空间不够使用的时候,用来“扩容”的,它的参数有两个,第一个就是我们指向我们申请下来那块空间起始地址的指针,第二个就是“扩容”多少个字节,同样,返回的是申请到空间的起始地址,但是如果我们原空间后面的空间不够“扩容”的字节,就会重新找一片空间,然后把原空间的数据拷贝过去,同样,如果申请失败会返回空指针,所以对于我们要“扩容”的时候,最好是在创建一个指针变量,先用新创建的指针变量接收realloc的返回值,然后判断,申请成功就把realloc申请下来的空间赋值给我们的原指针。
int main()
{
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;
return 0;
}
柔性数组
柔性数组,于C99标准中定义,它处于结构体当中,当我们结构体的最后一个成员是一个未知大小的数组的时候,就是我们的柔性数组,当然,这个结构体一定要有其他的成员,可能看字看不懂?那我们直接上代码。
struct st_type
{
int i;
int a[0];//柔性数组
};
这种写法可能在有的编译器上跑不过,那我们就换一种写法。
struct st_type
{
int i;
int a[];//柔性数组
};
柔性数组特点
我们前面提到,柔性数组之前必须至少有一个成员,那是为什么呢?对于柔性数组,如果用sizeof计算它的大小会是多少呢?我们用编译器试一下。
我们可以看到,整个结构体大小是4,那为什么是4?一个int就占用了四个内存了,何况我们还有一个数组,这里就直观的说明了,对于我们的柔性数组成员,它是不计入结构体大小的,如果这个结构体就它一个成员,那它大小就是0,那不方便我们进行后面的操作。同样对于我们柔性数组来说,它的内存占用也是用我们的malloc来申请,它申请的时候不和我们上面申请的时候是一样的,它需要先将我们结构体内其他成员的空间先跨过,才是自己的。
type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
柔性数组的优点
方便内存释放
当里面的成员不是柔性数组而是指针的时候,在使用的过程中,我们对其内部进行了二次的内存分配,而且把整个结构体的指针返回去了,别人使用这个结构体指针,他并不知道里面进行了二次的内存分配,他直接用free释放了整个结构体,不知道要对内部进行free,但是要是柔性数组,就没有这个问题,free一次就行了。
提高我们的访问速度
因为我们申请下来的空间是一片连续的空间,是更加有利于访问的。
总结
动态内存大大的提高了对内存空间的利用,避免了一些不必要的内存浪费。