C语言中开辟内存有很多种方式,目前我们最常用的也就是数组,但数组是在我们用到他之前就得设定好它的长度,有时很不方便。 我们知道,c语言规定,不允许设定一个未知长度的数组。(但在Linux下可以设定,但也不支持这样做)
例如:
int x = 5;
int arr[x];
这样的代码是不正确的,为了填补这一缺口,c语言有了动态内存,c语言提供了几个函数来管理我们的动态内存,这几个函数非常重要,分别为:
1:malloc
void* malloc(size_t size);
可以看到这个函数的返回类型为void*,为一个空指针(我们前面了解到,void* 可以作为返回值和传参,但不能直接解引用,所以我们在运用它时需要先将它强制转化为我们想要的指针类型);
- 这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针;
- 如果开辟成功,则返回一个指向这块空间的地址;
- 如果开辟失败,则返回NULL,因此对malloc函数的返回值一定要先检查再使用;
我们知道,以前我们定义的局部变量、函数、数组所需要的空间都是在栈上开辟的,但切记malloc申请的内存是在堆上开辟的。栈上的可用资源远远小于堆上的,所以malloc的专长是用来提供大内存需求,而且方便管理;
堆区向上生长,栈区向下生长
当我们用malloc函数开辟空间时,就会在堆上开辟一段连续的内存空间。
2.free
void free(void* ptr);
有了开辟内存,则就必须要有释放内存!
c语言提供了这样一个函数来释放我们所开辟的内存;
注意:如果ptr不是指向动态内存的,则这种行为未定义;如果ptr为NULL,则函数什么都不会做;
栗子:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* ptr = NULL;
int num = 0;
scanf("%d", &num);
ptr = (int*)malloc(num * sizeof(int));
if (NULL != ptr) //必须判断
{
int i = 0;
for (i = 0; i < num; i++)
{
*(ptr + i) = i;
}
}
else
{
perror("malloc");
}
int* p = ptr;
for (; p < ptr + num; p++)
{
printf("%d\n", *p);
}
free(ptr); //勿忘
ptr = NULL; //防止ptr成为野指针;
return 0;
}
3.calloc
void* calloc(size_t num, size_t size);
该函数的功能为为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为零;
该函数与malloc函数基本上没有什么区别,唯一区别就是可以把开辟的内存自动初始化;(初始化每个字节为0)
如果要求申请内存需要初始化,这个函数就会很方便。
4.realloc
此函数可以帮助你对申请的内存进行扩展或者缩减,让动态内存管理变得更加灵活。
参数介绍:
ptr是要调整的内存地址,size为调整之后的新大小;
返回值为调整之后的内存起始位置;
这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间;
realloc在调整内存时存在两种情况:
1.当原有空间的后面有足够大的空间的时候 : 要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化;
2.当原有空间的后面没有足够空间时 : 在堆空间上另找一个合适大小的连续空间来 使用。这样函数返回的是一个新的内存地址;
栗子:
#include<stdio.h>
int main()
{
int* ptr = malloc(100);
if (ptr != NULL)
{
;//执行任务
}
else
{
perror("malloc");
//或者 exit(EXIT_FAILURE);
}
//第一种方法扩展
//ptr = realloc(ptr, 1000); //第一种方法;(可能会造成内存泄漏)
//第二种方法扩展
int *p = realloc(ptr, 1024);
if (p != NULL)
{
ptr = p;
}
else
{
perror("realloc");
}
//执行任务
free(ptr);
p = NULL;
return 0;
}
我们在使用realloc时,一定要判断他返回的值,不然如果开辟失败,则会返回NULL,第一种方法的话,ptr就指向了NULL,原来的指针就找不到了,free不掉了,就造成了内存泄漏;