目录
1.前言
1.1内存分布
1.2动态内存的分配
2.四个重要的内存函数
2.1malloc和free
2.2calloc
2.3realloc
3.常见错误
3.1对NULL指针进行解引用操作
3.2对动态开辟内存的越界访问
3.3使用free释放非动态开辟的空间
3.4使用free释放了动态开辟内存的一部分
3.5对同一块动态内存开辟的空间多次释放·
3.6动态开辟的空间忘记释放,造成内存泄漏
4.参考
5.与c++的差别(malloc/free和new/delete的异同点)
c语言中内存大致可以分为六个区域,自上而下:内核空间,栈区,内存映射区,堆区,数据段,代码段
例:
int c = 1;//储存在数据段
void Test() {
static int a = 1;//储存在数据段
int b = 1;//储存在栈里
int num[10] = { 1,2,3,4 };//储存在栈里;sizeof(num) = 40
char char1[] = "abcd";//char1和char1*都储存在栈里;sizeof(char1)=5,strlen(char1) = 4
char* pchar2 = "abcd";//pchar2在栈里,*pchar在数据段里
int* ptr1 = (int*)malloc(sizeof(int) * 4);//ptr1储存在栈里,*ptr1在堆里
//解释上面的代码含义以及为什么需要用free释放:malloc等空间申请都是在堆上进行申请的,所以必须由free来进行释放。对应常见错误3.6(因为对堆上的空间由程序员自己来管理)
int* ptr2 = (int*)calloc(4, sizeof(int));
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
free(ptr1);
free(ptr3);
}
对于下列代码,申请的空间是固定的
int num=10; //向内存申请4个字节
int arr[10]={0}; //向内存申请40个字节
上述的开辟的空间的方式的特点:
1,空间开辟的大小是固定的
2,数组在申明时必须指定数组的长度,他所需要的内存在编译时分配。
其问题所在之处:对于数组arr[10],已经申请了40个字节的空间,如果部分没有使用就会造成浪费,或者不够,因此动态内存的分配很重要。
c语言提供了一个动态内存开辟的函数:void* malloc (size_t size);(此函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
#void是空类型,void*是空类型指针,又叫万能指针,就是该指针能接收任意类型的指针,可以指向任何类型对象,所以不能对空类型指针进行解引用,必须强制类型转换成相应的指针类型,才能进行解引用操作。空类型指针不能进行解引用操作;不能进行±整数运算。同时在c语言里,空指针NULL指的是地址为0的那块空间,故对NULL是不能进行解引用操作的(对应错误类型3.1)
c语言还提供了另一个函数free,专门用来做动态内存的释放与回收,void free(void* ptr);(用来释放动态开辟的内存)
malloc与free都声明在stdlib.h头文件中。
例:
int main()
{
int arr[10] = { 0 }; //向内存申请了10*4=40个字节的空间
void* p = malloc(50); //50字节
}
分析:在上面的void* p = malloc(50); 中,向内存空间申请了50个字节的空间,malloc会返回一个地址,将这个地址存入void*的指针里面去。
特别的,如果要将malloc开辟的40个空间里面放入10个整形,需要强制类型转换;需要将malloc开辟的void*类型的指针强制类型转换为:int *类型的指针。
int* p = (int*)malloc(50);
为什么必须释放开辟的动态内存空间:malloc是一个函数,表明了堆空间说在程序运行起来之后,再在系统上申请的,空间只申请不释放,会造成内存泄露。
c语言提供了一个函数叫calloc(用来动态内存开辟)void* calloc(size_t num,size_t size);
函数功能:为num个大小为size的元素开辟一块空间,并把空间的每个字节都初始化为0.
与malloc的区别:malloc没初始化,效率高;calloc做了初始化,效率低
void* realloc(void* ptr,size_t size);
ptr是要调整的内存地址;size是调整之后的新大小;返回值为调整之后的内存起始地址;
这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移到新的空间;
realloc在调整内存空间存在两种情况:
1,原有空间之后有足够大的空间
2,原有空间之后没有足够大的空间
int main()
{
int* p = (int*)malloc(40);
*p = 0;// 万一malloc失败了,p就被赋值为NULL
free(p);
p = NULL;
return 0;
}
解决方法:在申请空间后对p进行判断
if(p==NULL)
return 1;
#include
int main()
{
int *p = (int*)malloc(10*sizeof(int));//申请10个int空间大小
if(p==NULL)
return 1;
int i=0;
// 越界访问
for(i=0;i<40;i++)
{
*(p+i)=i;
}
}
int main()
{
int arr[10]={0}//存于栈区
int *p=arr;
free(p);//使用free释放了数组申请的空间
p=NULL;
return 0;
}
int main()
{
int* p=(int*)malloc(10*sizeof(int));
if(p==NULL)
return 1;
int i=0;
for(i=0;i<5;i++)
{
*p++ = i;
}
free(p);
p=NULL;
return 0;
}
由于不当操作使得原本指向动态内存空间的p的指向发生改变,不能读取到完整的空间,如果使用free就会造成错误
注:p指向开辟内存的首地址,而在循环赋值中,p只对其中5个int大小进行了赋值,而后就对该空间释放。除此之外p受++运算符不断向后移动,使得p的初始位置改变,这很容易导致内存泄漏。
int main()
{
int* p=(int*)malloc(100);
//使用
//释放
free(p);
p=NULL;
//释放
free(p);
return 0;
}
由于记忆问题导致在对一块动态内存空间释放后再次释放,造成错误
由于不断申请动态内存空间而忘记回收,造成每隔一段时间内存满了程序卡死,就是内存泄漏
解决方法:在该指向这块空间的指针的生命周期结束前使用free函数释放
C/C++动态内存开辟详解(含常见错误以及经典面试题)_利刃Cc的博客-CSDN博客_c++开辟内存
内存管理---malloc/free和new/delete的区别_Exy-的博客-CSDN博客_malloc/free和new/delete的区别
malloc/free和new/delete的共同点是:
都是从堆上申请空间,并且需要用户手动释放。
malloc/free和new/delete不同点是: