前言
🌈hello! 各位宝子们大家好啊,又是新的一天开始了,今天给大家带来的是动态内存规划这一章节!
⛳️我们在创建变量的时候大家都知道大小是固定,不够灵活。而动态内存分配可以改变这一现象!当我们需要多少就可以规划多少,而不需要时就可以释放掉,这样是不是就可以极大地避免了内存的浪费!
📚本期文章收录在《C语言高阶篇》,大家有兴趣可以看看呐!
⛺️ 欢迎铁汁们 ✔️ 点赞 👍 收藏 ⭐留言 📝!
💬 为什么存在动态内存分配
⛳️在前面内容中我们学的开辟空间大多都是用数据类型直接创建空间。
- 比如用整形开辟一个大小为4个字节的空间
- 或者数组开辟一个连续的储存空间
- 而这些临时变量大多都是存放在栈区的
🔥 注:在前面C/C++中内存大致分的三个区域有讲过《C/C++的三个内存区域》
int main() { int a = 0;//在栈空间上开辟四个字节 int arr[40]={0};//在栈空间上开辟40个字节 }
但是这的开辟空间的方式有两个缺点:
- 数组空间申请多了,如果没有用完就会照成空间的浪费!
- 空间开辟大小是固定的
所以像以前的空间开辟方法满足不了我们的需求,那么有没有我们想开辟多少空间就开辟多少,而当我们不想要的时候还可以释放!这个时候就需要动态内存开辟了!
💬 动态内存函数的介绍
⛳️ 而动态内存开辟就需要用到相关的函数分别是:
malloc
free
calloc
realloc
把这四个函数只要掌握就可以完全的掌握动态内存分配了,下面我们就详细给大家介绍介绍:
1️⃣ 动态内存函数 malloc
动态内存开辟的函数:malloc
void* malloc (size_t size);
这个函数向内存申请一块 连续可用
的空间,并返回指向这块空间的指针。
- 如果开辟成功,则返回一个指向开辟好空间的指针。
- 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
- 返回值的类型是
void*
,所以malloc
函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。 - 如果参数
size
为0
,malloc
的行为是标准是未定义的,取决于编译器。
⛳️ 好了malloc的使用方法给大家介绍了,接下来就是给大家介绍介绍这个这个函数如何使用:
- 他们的库函数都是
#include <stdlib.h>
- 所以使用的时候一定要记得加头文件哦!
#include <stdio.h> #include <stdlib.h> int main() { int arr[10] = { 0 }; malloc(40); return 0; }
我们都知道数组创建的空间是连续,而malloc申请的空间也是连续的但是malloc的空间是没有类型的。
- 那么我们想像数组一样访问整形4个字节来访问怎么办呢?
- 很简单我们把
malloc
的返回值类型强制转换为int*
- 拿整形指针接收
malloc
的返回值就可以
#include <stdio.h> #include <stdlib.h> int main() { int arr[10] = { 0 }; int* p=(int*)malloc(40); }
这样我们就可以和整形数组一样存放整形了,因为指针解引用每次也跳过4个字节
💭 malloc 函数返回失败怎么办
如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
- 如果开辟失败,就会给
p
返回NULL 空指针。 - 而我们一旦对空指针在进行访问不会,越界访问越界了嘛?
- 而这是绝对不允许的,一旦越界就会导致程序崩溃⁉️
- 所以我们加一段代码来保证程序的安全性
int main() { int arr[10] = { 0 }; int* p = (int*)malloc(40); //开辟失败 if (p == NULL) { perror("malloc"); return 1; } return 0;
这样就就可以在开辟失败时及时避免错误,直接return返回让程序结束!
- 这里开辟失败是,让库函数
perror
给我们提示一下malloc
里面出现了什么错误!- 下面就给大家观察一下开辟失败是什么样的
📑图片展示:
⛳️ 大家看这里当我们申请的空间太大是开辟不了就会给我们返回空间不够的错误提示
- ps:申请的空间一定要非常大不然测试就不会返回错误值的
- 博主试了好几遍还以为是自己的代码问题结果是申请空间太小了
💭 malloc 是在哪里开辟空间的
⛳️我们都知道临时变量是存放在栈空间的,那么malloc申请的空间是哪里的呢?
📚 代码演示:
#include <stdio.h> #include <stdlib.h> int main() { int arr[10] = { 0 }; int* p = (int*)malloc(40); //开辟失败 if (p == NULL) { perror("malloc"); return 1; } int i = 0; for (i = 0; i < 10; i++) { printf("%d\n", p[i]); } return 0; }
📑 代码结果:
⛳️这里打印的就是我们申请空间的值,但由于malloc函数并不会给我们初始化所以里面存放的都是随机值。
- 那么这里面的动态内存分布到底是什么样呢?
- 为什么里面全部都是随机值呢?
- 这个图片来告诉你一切
⛳️我们动态内存分配都是在堆区开辟空间的,
p
指针变量是在栈区里面开辟的空间里面。所以当malloc在返回时返回了起始地址然后我们用 p 接收了malloc申请空间的起始地址
- 但是,malloc这个函数只返回起始地址并不进行初始化
💭 malloc申请空间为0
⛳️ 做为一个程序员我们在想要申请空间的时候肯定是已经知道,要申请多少空间。你又要malloc申请空间,又只申请0个空间,这种行为本来就是不合理,所以我们在使用malloc时要避免这种情况以免出现不必要的错误!
- 如果参数
size
为0,malloc
的行为是标准是未定义的,取决于编译器。
📆 malloc申请空间会主动释放嘛
⛳️而malloc申请的空间,当程序退出时,才会还给操作系统,而当程序未结束时,动态内存申请的内存空间,是不会主动释放的。这样就会照成内存的浪费!
- 这时就需要使用free来释放,我们申请的动态内存空间
- 编程的好习惯是,每次使用完malloc都要使用free释放空间
- 下面我们就来介绍一下free函数
2️⃣ 动态内存函数 free
⛳️C语言提供了另外一个函数 free ,专门是用来做动态内存的释放和回收的,函数原型如下:
- void free (void* ptr);
free函数用来释放动态开辟的内存。
- 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
- 如果参数 ptr 是NULL指针,则函数什么事都不做。
⛳️ 好了free的参数详情给大家介绍了,接下来就是给大家介绍介绍这个这个函数如何使用:
📚 代码演示:
#include <stdio.h> #include <stdlib.h> int main() { int arr[10] = { 0 }; int* p = (int*)malloc(40); //开辟失败 if (p == NULL) { perror("malloc"); return 1; } int i = 0; for (i = 0; i < 10; i++) { printf("%d\n", p[i]); } free(p); p = NULL; return 0; }
⛳️ 这就是
free
的使用方法了,是不是非常简单。只需要把我们指针变量p
传给free
函数,因为p
里面存放了malloc
申请空间的起始地址,那么为什么还要把 p 给置为空指针呢?
- 因为我们虽然把指针p记录的动态空间给释放了
- 但是p本身不会被释放,而p里面存放的地址就成 野指针!
- 这个情况是非常不安全的所以我们把它置为空!