延吉有没有做网站的,今科云平台网站建设,网页版 微信,免费广告制作软件有些情况下需要开辟的空间大小在程序运行过程中才能确定下来#xff0c;而常规的在栈区开辟空间是在编译时就分配好了内存#xff0c;并且内存大小不能改变#xff0c;因此需要引入动态内存分配#xff0c;动态内存分配的内存是在堆区#xff0c;需要由用户手动开辟#… 有些情况下需要开辟的空间大小在程序运行过程中才能确定下来而常规的在栈区开辟空间是在编译时就分配好了内存并且内存大小不能改变因此需要引入动态内存分配动态内存分配的内存是在堆区需要由用户手动开辟手动释放并且可以由用户自行改变空间的大小。下面介绍一下动态内存分配的相关函数
malloc和free malloc是c语言中的动态内存开辟函数用于在堆区动态开辟一块连续的内存返回指向这块内存的指针其函数原型如下
//size是要开辟的字节数返回开辟空间的地址如果开辟失败则返回NULL
//返回值是void*类型malloc不知道开辟的空间是什么类型需要用户自己强制类型转换成需要的类型
void* malloc(size_t size); free是专门用来做动态内存的释放和回收的函数函数原型如下
//ptr指向动态开辟的空间
void free(void* ptr); 如果malloc的size为0是标准未定义的取决于编译器。 如果free的ptr指向的空间不是动态开辟的也是标准未定义的。 如果free的ptr是NULL则什么都不做。 malloc和free都声明在stdlib.h头文件中。
#include stdio.h
#include string.h
#include stdlib.h
int main()
{int num 0;scanf(%d, num);//键盘输入要开辟的数组大小int *ptr (int *)malloc(num * sizeof(int));//动态内存开辟int i 0;for (i 0; i num; i)//为开辟的空间赋值{ptr[i] i;}for (i 0; i num; i)//打印{printf(%d , ptr[i]);}free(ptr);//释放开辟的空间防止内存泄漏ptr NULL;//将指针置空防止野指针return 0;
} free只是把ptr指向的空间还给了内存不能再通过ptr访问原来的空间但是ptr里面存的还是原来的地址而这个地址里边存放的东西已经不确定了ptr就变成了野指针因此内存释放后需要对源地址置为空指针防止产生野指针。
calloc calloc函数也用来动态分配内存和malloc的区别是calloc会在返回地址前把申请的空间中的每个字节初始化为0其函数原型如下
//size是要开辟的元素的大小num是要开辟的元素个数如果成功则返回指向开辟空间的指针失败返回NULL
void* calloc(size_t num, size_t size);
#include stdio.h
#include string.h
#include stdlib.h
int main()
{int num 0;scanf(%d, num);//要开辟元素的个数int *ptr (int *)calloc(num, sizeof(int));//在堆区开辟num个intint i 0;for (i 0; i num; i){printf(%d , ptr[i]);//打印应该是全0因为calloc会把开辟的元素初始化为0}free(ptr);ptr NULL;return 0;
}
realloc realloc主要用来调整动态开辟的内存的大小其函数原型如下
//ptr是要改变大小的空间的地址size是改变后的大小如果改变成功则返回改变后的空间地址失败则返回NULL
void* realloc(void* ptr, size_t size); realloc在调整空间大小时有以下两种情况
①原有空间之后有足够大的空间供扩容这种情况就直接在原空间后面追加空间原空间中的数据也不会改变返回的地址就是原空间的地址。
②原有空间之后没有足够大的空间供扩容这种情况下会在堆空间上另找一块合适大小的连续空间作为新的空间会将原空间的数据拷贝到新空间并释放原空间返回的地址是新空间的地址。
int *ptr (int*)malloc(100);//开辟100字节的堆空间
if(ptr ! NULL)
{
//业务处理
}
//扩展容量
//代码1
ptr (int*)realloc(ptr, 1000);//这样可以吗(如果申请失败会如何)如果拓展失败会把原指针变成NULL因此需要先用一个新的指针接收扩展后的地址如果开辟成功再赋给原变量
//代码2
int*p NULL;
p realloc(ptr, 1000);//用新指针接收拓展后的地址
if(p ! NULL)
{ptr p;//返回值不为空再把新指针赋给原指针
}
//业务处理
free(ptr);
ptr NULL;
return 0;realoc(NULL,40);//等价于malloc(40)
动态内存常见错误 在开辟和使用动态内存时可能会出现以下一些错误需要注意并且避免
①对NULL的解引用操作
int main()
{int* p (int*)malloc(sizeof(INT_MAX));//动态内存开辟if(NULL p)//所以在内存开辟后要判断开辟是否成功{return 1}*p 10;//这里如果内存开辟失败p就是NULL解引用NULL会出错free(p);p NULL;return 0;
}
②对动态开辟空间的越界访问
int main()
{int* p (int*)calloc(10,sizeof(int));if(NULL p){return 1;}for(int i 0; i 10; i)//这里会访问到p[10],然而只开辟了十个int的空间会越界{p[i] i;}free(p);p NULL;return 0;
}
③对非动态开辟的内存用free释放
int main()
{int a 10;int* p a;free(p);//这里的p指向的是栈区空间不能用free释放会崩溃
}
④使用free释放动态开辟空间的一部分
int main()
{ int* p (int*)calloc(10,sizeof(int));p;free(p);//上面改变了p指向的空间不再指向开辟内存的起始地址这样释放会崩溃所以不要篡改申请的堆空间的起始地址
}
⑤对一块动态内存多次释放
int main()
{int* p (int*)malloc(sizeof(int));free(p);free(p);//上面已经释放了内存p已经变成了野指针再次释放程序会崩溃
}
int main()
{int* p (int*)malloc(sizeof(int));free(p);p NULL;//这里就突出了释放空间后将指针置空的重要性free(p);//上面已经释放了内存但是p被置成了NULL再次释放什么都不做不过还是不建议二次释放
}
⑥动态开辟内存忘记释放
void test()
{int* p (int*)malloc(4);int flag 0;scanf(%d,flag);if(3 flag)//如果这里输入了3函数就会直接return不会执行freep申请的内存就不会释放造成内存泄漏return;free(p);p NULL;return;
}如果动态开辟的内存使用完不用free释放的话这段空间就会内存泄漏就相当于自己不用也不给其他人用造成空间浪费。
经典例题
void GetMemory(char* p)
{p (char*)malloc(100);
}
int main()
{char* str NULL;GetMemory(str);strcpy(str,hello world);printf(str);
}
//上述代码的问题如下
//str是实参p是形参是传值调用所以改变p不会改变strGetMemory结束后p被销毁
//但是开辟的堆空间没释放造成内存泄露而str还是NULLstrcpy对NULL解引用会崩溃。
//应该按以下方法写改成传地址,并且使用完要释放。
void GetMemory(char** p)
{*p (char*)malloc(100);
}
int main()
{char* str NULL;GetMemory(str);strcpy(str,hello world);printf(str);free(str);str NULL
}
char* GetMemory(void)
{char p[] hello world;return p;
}
int main()
{char* str NULL;str GetMemory();printf(str);
}
//上述代码问题如下
//由于p是局部变量p中存的是数组首地址返回p后赋给str
//但是GetMemory退出后p指向的空间就不在了str指向的内容已经不是hello world了变成了野指针。
void test()
{char* str (char*)malloc(100);strcpy(str,hello world);free(str);//这里已经释放了str指向的空间if(str ! NULL){strcpy(str,hello);//这里的str已经变成了野指针printf(str);}
}