文章目录
- 🚀前言
- 🚀管理动态内存的函数
- ✈️malloc函数
- ✈️free函数
- ✈️calloc函数
- ✈️realloc函数
- 🚀在使用动态内存函数时的常见错误
- ✈️对NULL指针的解引用
- ✈️ 对动态开辟空间的越界访问
- ✈️对非动态开辟内存使用free释放
- ✈️使用free释放一块动态开辟内存的一部分
- ✈️对同一块动态内存多次释放
- ✈️动态开辟内存忘记释放(内存泄漏)
🚀前言
铁子们好啊!今天阿辉给大家讲一下C语言里面特别重要的一块知识——动态内存管理,为什么说它重要呢?相信大家在使用数组时都遇到过数字开辟空间太大或者太小的情况,这是很尴尬的因为数组空间一旦确定就难以扩大或者缩小。而今天的动态内存管理将帮我们很好的解决这一窘境。话不多说,开始咱们今天的学习吧!!!
🚀管理动态内存的函数
动态内存的管理其实也就分为两块
- 空间的开辟和扩容
- 空间的释放
而要做到以上两点,我们要引入几个重要的管理动态内存的函数
动态内存的开辟和释放都是在堆区上
✈️malloc函数
malloc
函数是用来开辟空间的,如何使用呢?下面是它的声明👇
void* malloc(size_t size);
关于malloc
函数的注意事项:
size
表示malloc
函数所开辟空间的大小,单位是字节maloc
函数对于开辟空间存放何种类型的数据并不关心,返回值是void*
类型,malloc
开辟空间成功,则返回这块空间的起始位置的地址;若开辟空间失败,则返回空指针NULL
- 给
malloc
传入的参数为0
时,这种行为是C语言标准未定义的,取决于编译器 - 由
malloc
函数开辟的空间,里面的内容是随机的
具体使用:
int mian()
{int* p = (int*)malloc(sizeof(int) * 10);//开辟10*4个字节的空间,返回值强转为int*if (p == NULL)//判断动态内存是否开辟成功{printf("malloc 开辟空间失败\n");//开辟失败返回调试语句,就是让程序员知道哪错了return 1;//开辟失败直接返回}//对动态开辟内存的进一步使用//这玩意其实就是个数组,有了首元素地址,不就是当成数组来玩嘛for (int i = 0; i < 10; i++)//十个元素初始化成0~9{p[i] = i;//这玩意与*(p+i) = i等价,不懂得可以看阿辉之前关于指针的文章}return 0;
}
关于malloc
函数开辟的空间,它是一块连续的空间,直接当成数组使用一点毛病没有,
它与数组最大的区别在于,malloc
开辟的空间是在堆区上的,而数组是在栈区上的。
而堆区上申请的空间须有程序员自己手动释放(Java,Python有垃圾回收机制),否则将造成内存泄漏。其实就是你从我这拿了空间去用,不用了就还给我,别占着茅坑不拉屎,你不用别人也用不了。对于堆区申请的空间的释放,这里要引入下面这个新的函数
✈️free函数
C语言提供了一个函数free,专门用来做动态内存的释放和回收的,函数声明如下:
void free(void* ptr);
对于free
函数使用时的注意事项:
- 对于传给
free
的参数必须是指向动态开辟内存(也就是是堆区开辟的空间)的起始地址的指针 - 若传给
free
的参数是空指针NULL
,free
函数则什么也不干 free
只会释放传入的指针指向的空间,并不会给传入指针置空,这会很危险,空间你都还给操作系统了,你这个指针还指向那里,这个指针很野得治,所以free
完,指针我们要置空
int mian()
{//开辟动态空间int* p = (int*)malloc(sizeof(int) * 10);//判断动态内存是否开辟成功if (p == NULL){printf("malloc 开辟空间失败\n");return 1;}/*对动态开辟空间的使用*///不用啦,释放空间free(p);p = NULL;return 0;
}
👆这段代码就是动态内存的使用的基本模式,先开辟空间,然后判断是否开辟成功,接着使用这块空间,最后不用了把这块空间释放掉
✈️calloc函数
C语言中还提供了calloc
函数,同样是进行动态内存分配的,函数声明如下:
void* calloc(size_t num, size_t size);
calloc
与malloc
仅有两点不同
- 使用上
calloc
函数需要两个参数,开辟空间为num
个大小为size
个字节的空间 calloc
函数开辟的空间会把每个字节全部初始化为0
int main()
{int* p = (int*)calloc(10, sizeof(int));if (p == NULL)//判断空间是否开辟成功{printf("calloc开辟空间失败\n");return 1;}for (int i = 0; i < 10; i++)//打印动态内存中的元素{printf("%d ", p[i]);}free(p);//空间释放p = NULL;//指针置空return 0;
}
输出:
0 0 0 0 0 0 0 0 0 0 //十个0
✈️realloc函数
铁子们估计有点不买账了,说好的动态内存分配,讲了这么多也没有体现动态俩字。铁子们别急,realloc
函数才是今天的重头戏
C语言提供realloc
函数用来使动态内存的使用更加灵活,之前开辟的空间太大或者太小都可以灵活的调整重新分配空间大小
realloc
函数的函数声明
void* realloc(void* ptr,size_t size);
realloc
函数在使用时要注意下面这几点:
-
ptr
是要调整的空间的起始地址,ptr
得是动态内存开辟的地址,若是ptr
为空指针,realloc
函数将与malloc
函数的实现一样 -
size
是realloc
函数调整大小后的内存空间大小 -
realloc
函数的返回值是空间调整后的起始地址,空间开辟失败返回空指针 -
realloc
函数扩容的空间里面的值并未被初始化,是随机值 -
这个函数在调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间
-
对于
realloc
函数调整空间有了两种情况:-
情况一:原有空间后有足够空间
-
情况二:原有空间后无足够空间,原有空间将被释放掉,重新找一块空间
-
例子:
int main()
{int* p = (int*)malloc(5 * sizeof(int));//先用malloc申请5个整型大小的内存空间if (p == NULL)//判断是否开辟成功{printf("malloc开辟空间失败\n");return 1;}int i = 0;for (i = 0; i < 5; i++){*(p + i) = 1;//把5个整型全部初始化为1}//不够用了,要再增加5个整型int* ptr = (int*)realloc(p, 10 * sizeof(int));if (ptr == NULL){printf("realloc开辟空间失败\n");return 1;}p = ptr;//空间扩容成功将ptr的地址赋给pptr = NULL;//ptr置空for (i = 0; i < 10; i++){printf("%d ", *(p + i));}free(p);p = NULL;return 0;
}
上述,使用realloc
对空间做调整时,一定要新创建一个变量来接收realloc
的返回值,因为如果使用指向原空间的起始地址的指针p
来接收,一旦realloc
开辟空间失败,p
将被赋值成NULL
,将找不到原来空间造成内存泄漏
🚀在使用动态内存函数时的常见错误
✈️对NULL指针的解引用
void test()
{int* p = (int*)malloc(INT_MAX / 4);*p = 20;//如果p的值是NULL,就会有问题free(p);
}
✈️ 对动态开辟空间的越界访问
void test()
{int i = 0;int* p = (int*)malloc(10 * sizeof(int));if (NULL == p){exit(EXIT_FAILURE);}for (i = 0; i <= 10; i++){*(p + i) = i;//当i是10的时候越界访问}free(p);
}
✈️对非动态开辟内存使用free释放
void test()
{int a = 10;int *p = &a;free(p);//error——不能对非动态内存进行释放
}
✈️使用free释放一块动态开辟内存的一部分
void test()
{int *p = (int *)malloc(100);p++;free(p);//p不再指向动态内存的起始位置
}
✈️对同一块动态内存多次释放
void test()
{int *p = (int *)malloc(100);free(p);free(p);//重复释放
}
✈️动态开辟内存忘记释放(内存泄漏)
void test()
{int *p = (int *)malloc(100);if(NULL != p){*p = 20;}
}
int main()
{test();while(1);
}
对于动态开辟的内存一定要记得释放,否则将造成内存泄漏
如果觉得文章对你有帮助的话,还请点赞,关注,收藏支持博主,如有不足还请指点,博主及时改正,感谢大家支持!!!