目录
我们已经掌握的内存开辟方式有:
- int val = 20;//在栈空间上开辟四个字节
- char arr[10] = {0};//在栈空间上开辟10个字节的连续空间
但是上述的开辟空间的方式有两个特点:
1. 空间开辟大小是固定的。
2. 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,
那数组的编译时开辟空间的方式就不能满足了。
这时候就只能试试动态存开辟了。
C语言提供了一个动态内存开辟的函数:
void* malloc (size_t size);
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
如果开辟成功,则返回一个指向开辟好空间的指针。
如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己
来决定。
如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

int* pf = (int*)malloc(10*sizeof(int));
这里就开辟了10个空间大小的int类型
C语言提供了另外一个函数free,专门是用来做动态内存的释放和回收的,函数原型如下:
void free (void* ptr);
free函数用来释放动态开辟的内存。
如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
如果参数 ptr 是NULL指针,则函数什么事都不做。
注:malloc和free都声明在 stdlib.h 头文件
例:
未释放malloc
- #include
- #include
- int main()
- {
- int* pf = (int*)malloc(10*sizeof(int));
- if (pf == NULL)
- {
- //打印错误
- perror("malloc");
- }
- int i;
- for (i = 0; i < 10; i++)
- {
- *(pf + i) = i;
- }
- for (i = 0; i < 10; i++)
- {
- printf("%d ", *(pf + i));
- }
-
- }
![]()

要养成一个好习惯,一旦进行开辟内存,一定要释放,不会会出现很多问题,后续我们会讲到
- #include
- #include
- int main()
- {
- int* pf = (int*)malloc(10*sizeof(int));
- if (pf == NULL)
- {
- //打印错误
- perror("malloc");
- }
- int i;
- for (i = 0; i < 10; i++)
- {
- *(pf + i) = i;
- }
- for (i = 0; i < 10; i++)
- {
- printf("%d ", *(pf + i));
- }
- //释放malloc
- free(pf);
- pf = NULL;
- }
C语言还提供了一个函数叫 calloc , calloc 函数也用来动态内存分配。原型如下:

1.函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。
2.与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。
区别:


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

ptr 是要调整的内存地址
size 调整之后新大小
返回值为调整之后的内存起始位置。
这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间
- #include
- #include
- int main()
- {
- int* p = (int*)calloc(10,sizeof(int));
- if (p == NULL)
- {
- //打印错误
- perror("malloc");
- return 1;
- }
- int i;
- for (i = 0; i < 10; i++)
- {
- printf("%d\n", *(p + i));
- }
- //假设10个整形空间不够,需要20个整形空间,这样写对吗?
-
- p=realloc(p, 20 * sizeof(int));
- free(p);
- pf = NULL;
- return 0;
- }
注:这里也有可能realloc开辟失败,如果继续存在在p中,会导致之前的数据也会丢失
应写成:
- int* pf=realloc(p, 20 * sizeof(int));
- if (pf != NULL)
- {
- p = pf;
- }
realloc在调整内存空间的是存在两种情况:


情况1:
当是情况1 的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。

情况2:
当是情况2 的时候,原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新的内存地址。

- #include
- #include
- int main()
- {
- int* p = (int*)malloc(10 * sizeof(int));
- *p = 20;//ok?
- }
这样写是否正确呢?
解析:这种写法是错误的,万一malloc开辟失败,不能对NULL指针进行解引用操作
正确写法:
- #include
- #include
- int main()
- {
- int* p = (int*)malloc(10 * sizeof(int));
- if (p == NULL)
- {
- perror(malloc);
- return 1;
- }
- *p = 20;
- free(p);
- p = NULL;
- return 0;
- }

本来开辟了10个整形,而却需要11个整形,这就越界了,所以系统会崩溃

提示:free函数只能释放malloc,calloc,realloc这种开辟出来的内存,而这里a是在栈空间上开辟的内存,所以也是错误。


解析: 现在p指向第六个位置,不能中通开辟,必须要把所有开辟的空间使用完,或者 让p指向起始位置,不能都会出现错误。
注:这里强调一般开辟好了p,最好不要动p的位置,可以再定义一个指针来接收p
- #include
- #include
- int main()
- {
- int* p = (int*)calloc(10 ,sizeof(int));
- if (p == NULL)
- {
- perror(calloc);
- return 1;
- }
- int* ppp = p;
- int i;
- for (i = 0; i < 5 ; i++)
- {
- *ppp= i;
- ppp++;
- }
- free(p);
- p = NULL;
- return 0;
- }

解析:不能进行多次释放,如果释放了一定要把指针置为NULL指针,这样也不会出现错误。

- #include
- #include
- void test()
- {
- int* p = (int*)malloc(100);
- if (1)
- {
- return 0;
- }
- free(p);
- p = NULL;
- }
- int main()
- {
- test();
- while (1);
- }
解析:进入到test函数里面,开辟了100个空间的p,但if判断为真了,直接返回到main函数里面,test函数里面的malloc根本来不及释放,这就会导致了内存泄漏的问题 ,根本来不及释放,所以那段空间就直接浪费了,找也找不到。
正确写法:
- #include
- #include
- int* test()
- {
- int* p = (int*)malloc(100);
- if (1)
- {
- return p;
- }
- free(p);
- p = NULL;
- }
- int main()
- {
- int* ret=test();
- free(ret);
- ret = NULL;
- while (1);
- }
解析:万一来不及释放,我们可以返回它的地址,到main函数里面释放 。
忘记释放不再使用的动态开辟的空间会造成内存泄漏。
切记:
动态开辟的空间一定要释放,并且正确释放。
- #include
- #include
- void GetMemory(char* p)
- {
- p = (char*)malloc(100);
- }
- void Test(void)
- {
- char* str = NULL;
- GetMemory(str);
- strcpy(str, "hello world");
- printf(str);
- }
- int main()
- {
- Test();
- }

解析: 很明显的错误,进入到GetMemory函数中,malloc开辟了p之后,退出了这个函数p也被销毁了,所以str还是NULL指针
错误1:对str NULL解引用操作,程序崩溃
错误2:malloc未的及释放
正确写法:
- #include
- #include
- void GetMemory(char** p)
- {
- *p = (char*)malloc(100);
- }
- void Test(void)
- {
- char* str = NULL;
- GetMemory(&str);
- strcpy(str, "hello world");
- printf(str);
- free(str);
- str = NULL;
- }
- int main()
- {
- Test();
- }
把str的地址放进去,改动的就是str;
- #include
- #include
- char* GetMemory(char* p)
- {
- p = (char*)malloc(100);
- return p;
- }
- void Test(void)
- {
- char* str = NULL;
- str=GetMemory(str);
- strcpy(str, "hello world");
- printf(str);
- free(str);
- str = NULL;
- }
- int main()
- {
- Test();
- }
接收p的地址
- #include
- #include
- char* GetMemory(void)
- {
- char p[] = "hello world";
- return p;
- }
- void Test(void)
- {
- char* str = NULL;
- str = GetMemory();
- printf(str);
- }
- int main()
- {
- Test();
- }

解析:这里可以看见虽然str已经拿到p的地址,但由于p是局部变量,p的内容出来函数已经销毁了,所以找不到“hello world”这块内容了,str已经是野指针了,打印的是一些随机值

- #include
- #include
- void GetMemory(char** p, int num)
- {
- *p = (char*)malloc(num);
- }
- void Test(void)
- {
- char* str = NULL;
- GetMemory(&str, 100);
- strcpy(str, "hello");
- printf(str);
- }
- int main()
- {
- Test();
- }

解析:虽然打印出来了,但是少了free函数,记得开辟完一定要释放
- #include
- #include
- void Test(void)
- {
- char* str = (char*)malloc(100);
- strcpy(str, "hello");
- free(str);
- if (str != NULL)
- {
- strcpy(str, "world");
- printf(str);
- }
- }
- int main()
- {
- Test();
- }

解析:别看打印出来了,其实有很多的错误

虽然free(str)函数,但并没有把它置为NULL指针,所以str现在为野指针了 ,所以依然打印出来了
本篇文章详细介绍了四个动态内存函数,以及常见的动态内存错误,几个经典的笔试题
看到这里的铁铁们,要是对你们有帮助的话,可以给小编一个大大的三连!!!
如果有不对的地方,可以私信我,谢谢各位大佬们!!!