赞
踩
✨✨✨学习的道路很枯燥,希望我们能并肩走下来!
编程真是一件很奇妙的东西。你只是浅尝辄止,那么只会觉得枯燥乏味,像对待任务似的应付它。但你如果深入探索,就会发现其中的奇妙,了解许多所不知道的原理。知识的力量让你沉醉,甘愿深陷其中并发现宝藏。
本篇对动态内存总结,进一步了解动态内存函数
malloc,calloc,realloc.free.如有错误,请在评论区指正,让我们一起交流,共同进步!
本文开始
我们已掌握的内存开辟放式:固定方式
int val = 20; //=》申请4个字节空间
char arr[10] = { 0 };// =》申请10个内存空间
特点:
对于固定的空间大小,如果我们想要扩大或者缩小空间,固定的空间就满足不了我们的需求。这时候就需要动态开辟。
C语言提供的动态内存开辟的函数malloc
malloc: 函数向内存申请一块连续可用的空间,并返回指向这块空间的指针
void* :返回类型(开辟空间的起始地址返回到void*)
size_t size: 需要开辟的字节内存大小
为什么是void ?*
只知道开辟的总空间大小,不知道开辟空间大小的类型是什么,所以为了更通用使用void* 来维护。
int main()
{
void* p = malloc(40);
return 0;
}
上述是使用malloc开辟40个字节内存空间,但是我们一般不这样使用,我们如果解引用void* 不知道访问几个字节空间,+1,-1也无法知道内存移动几个字节空间,所以我们一般指定类型如下:
int* p = (int*)malloc(40);
int* p1 = (int*)malloc(10*sizeof(10));
内存如果开辟失败返回NULL,所以开辟空间后一定要判断一下返回不为空。
但是当开辟空间过大时,会产生错误如下图:
动态内存空间开辟时在堆上开辟,如果不主动释放,会一直占用空间(程序不结束的时候)。
总结:
free:内存释放函数
void* ptr:想要释放空间的地址
注意:我们释放的是开辟空间的首地址,如果没有保存首地址,放入的是已经移动的地址,会产生释放失败的情况。(保留开辟空间的首地址)
在free()空间后p指向的还是起始开辟空间的地址(不置空P为野指针),此时如果有人,再次访问这块空间会发生非法访问,所以释放完必须将指针置NULL(代表0);
开辟并释放空间代码示范:
#include<stdio.h> #include<errno.h> #include<string.h> int main() { //void* p = malloc(40); int* p = (int*)malloc(2147483647); //INT_MAX == 2147483647 int* ptr = p; if (p == NULL) { printf("%s\n", strerror(errno)); return 1; } //使用 int i = 0; for (i = 0; i < 10; i++) { *ptr = i; ptr++; } //释放 free(p); // p = NULL; // 置空 return 0; }
不释放空间,内存会一直被占用。所以我们开辟空间后,一定要记得释放空间
总结:
free函数用来释放动态开辟的内存。
calloc也可以用来动态内存分配
num: 开辟元素的个数
size:每个元素的大小(字节)
int main() { int* p = (int*)calloc(10, sizeof(int)); if (p == NULL) { perror("calloc"); return 1; } int i = 0; for (i = 0; i < 10; i++) { *(p + i) = i; } //释放 free(p); p = NULL; return 0; }
calloc特点:
realloc:改变ptr指向的空间大小为size(字节);
int main() { int* p = (int*)malloc(40); if (p == NULL) { return 1; } //使用 int i = 0; for (i = 0; i < 10; i++) { *(p + i) = i; } for (i = 0; i < 10; i++) { printf("%d ", *(p + i)); } //增加空间 int* ptr = (int*)realloc(p, 80); if(ptr != NULL) { p = ptr;//要维护p,所以赋值 ptr = NULL;//不用ptr让ptr为空 } free(p); p = NULL; // return 0; }
调整空间的两种情况:
realloc(NULL,40); == malloc(40);//realloc其中传递空指针时与malloc一样。
释放内存是对于动态开辟的内存
int main()
{
//局部变量大括号内创建,出大括号销毁
int num = 10;
int* p = #
//不需要释放
free(p);
p = NULL;
return 0;
}
释放空间一定要记得空间起始位置
int main() { int* p = (int*)malloc(40); if (p == NULL) { return 1; } int i = 0; for (i = 0; i < 5; i++) { *p = i; p++; } //释放 //释放时P已经不再指向动态内存开辟空间的起始地址了,free会失败 //释放空间一定要记得空间起始位置 free(p); p = NULL; return 0; }
不置空,重复释放会出现错误
int main() { int* p = (int*)malloc(40); if (p == NULL) { return 1; } int i = 0; for (i = 0; i < 5; i++) { *(p + i) = i; } free(p); //p = NULL; free(p); //如果上述p置空,此时可释放,相当于释放NULL作用不大 return 0; }
动态开辟的空间一定要释放,并且正确释放
释放是指:ptr指向的空间
使用get_,memory()函数开辟动态内存空间,但是忘记释放,会造成内存泄漏。
int* get_memory()
{
int* p = (int*)malloc(40);
return p;
}
int main()
{
int* ptr = get_memory();
//使用ptr
//没有释放
return 0;
}
开辟动态内存空间,不确定是否开辟空间成功,必须if(p !=NULL)判断开辟空间不为空。
int main()
{
int* p = (int*)malloc(40);
*p = 20;
free(p);
return 0;
}
越界访问动态内存空间
int main()
{
int* p = (int*)malloc(40);
int i = 0;
for (i = 0; i <= 10; i++)
{
*(p + i) = i;
}
free(p);
p = NULL;
return 0;
}
通过几个经典题目再熟悉一下动态开辟出现的错误。
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函数,str指向的为空,解引用空指针错误。
②malloc开辟的空间没有释放,存在内存泄漏。
图解:
改进代码(传地址):
p存原来地址,但原有空间已经被释放,再次访问属于非法访问空间。
char* GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
int main()
{
Test();
}
图示:
free() 完没有置空,指针为野指针
void Test(void) { char *str = (char *) malloc(100); strcpy(str, "hello"); free(str); if(str != NULL) { //str是野指针,这里是非法访问 strcpy(str, "world"); printf(str); } } int main() { Test(); }
✨✨✨各位读友,本篇分享到内容是否更好的让你理解了动态内存开辟,如果对你有帮助给个
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。