int a = 10;//在栈空间开辟了4个字节
int arr[10];//在栈空间连续开辟了10个连续的4字节空间
上面的两行代码开辟空间的方式均是开辟了大小固定的空间。
但是我们实际对空间的需求,不仅仅是上诉的情况。而是随着程序运行的时候我们才知道我们需要的空间要多大,
那数组的编译时开辟空间的方式就不能买满足了。
这时候就需要动态开辟内存了。
动态内存函数的介绍:malloc/calloc/realloc/free
malloc/free
c语言提供了一个动态内存开辟的函数:void* malloc(size_t size);
这个函数向内存申请了一块连续可用的空间,并返回指向这块空间的指针。
1)如果开辟成功,则返回一个只想开辟空间的指针。
2)如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
3)返回值的类型是void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候需要使用者自己来决定。
4)如果参数size 为0,malloc的行为是标准的未定义的,有编译器决定。
c语言提供了一个跟他匹配只用的函数free,专门是用来做动态内存的释放和回收,函数原型:
void* free(void* ptr);
free函数用来释放动态开辟的内存。
1)如果参数ptr 指向的空间不是动态开辟的,那么free函数的行为是未定义的。
2)如果参数ptr 是NULL 指针,则函数什么事都不做。
举个栗子
int main()
{
int* ptr = NULL;
ptr = (int*)malloc(sizeof(int)*10);
if (ptr != NULL) {
for (int i = 0; i < 10; i++) {
*(ptr + i) = 0; //初始化
printf("%p \n",(ptr+i)); //地址
printf("ptr的值:%d \n", *(ptr + i));//值
}
}
printf(" free(ptr); \n");
free(ptr); //释放空间
for (int i = 0; i < 10; i++) {
printf("%p \n", (ptr + i));
printf("ptr的值:%d \n", *(ptr + i));
}
printf(" ptr = NULL; \n");
ptr = NULL; //原指针置空
for (int i = 0; i < 10; i++) {
printf("%p \n", (ptr + i));
// printf("ptr的值:%d \n", *(ptr + i));
}
return 0;
}
我们看一下运行结果:
free();后的ptr真正存在的意义仅仅是几个连续的地址,且每个地址没有相应的空间。置空后的ptr地址变成了内存里最开始的几个连续地址。
calloc
c语言还提供了一个函数calloc,它也是用来动态内存分配的
函数原型: void* calloc(size_t num,size_t size);
1)函数的功能是为num个大小为size的元素开辟一块空间,并把空间的每一个字节初始化为0。
2)与函数malloc的区别只在于calloc会返回地址前把申请的空间的每个字节都初始化为0;
如果我们对申请的空间自动初始化,我们用calloc就显得非常方便了。
图例:
realloc
有的时候我们发现我们以前开辟的空间不够,太小了。或者我们我们之前开辟的空间太大了,为了合理的使用内存
我们就需要对内存的大小做一些灵活的调整。那么realloc 函数就可以做到对 开辟内存大小的调整。
函数原型:void* realloc( void* ptr ,size_t size);
1)ptr是要调整的内存地址。
2)size是调整后的大小。
3)返回值为调整后的内存起始位置。
4)这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间里。
5)realloc在调整内存空间的时候存在两种情况:
情况1.原有空间之后有足够大的空间。
情况2.原有空间之后没有足够大的空间。
举个栗子:
6)开辟成功返回原地址或者新开辟的空间的地址;失败返回NULL,所以我们要在使用动态开辟的内存之前,要先检查一下是否开辟成功,即ptr!=NULL,我们才能使用。
注意:没有释放不再使用的动态开辟的内存空间会导致内存泄漏!
动态开辟空间以后,不用以后一定要释放,并且正确释放!