如何免费虚拟网站,微信微网站平台,wordpress 备案,轻创优选地推app他治愈了身边所有人#xff0c;唯独没有治愈他自己—超脱 csdn上的朋友你们好呀#xff01;#xff01;今天给大家分享的是动态内存管理 #x1f440;为什么存在动态内存分配 我们定义的局部变量在栈区创建 int n  4;//在栈上开辟4个字节大小int arr[10]  { 0 };//在栈上开…  他治愈了身边所有人唯独没有治愈他自己—超脱 csdn上的朋友你们好呀今天给大家分享的是动态内存管理 为什么存在动态内存分配 我们定义的局部变量在栈区创建 int n  4;//在栈上开辟4个字节大小int arr[10]  { 0 };//在栈上开辟连续的40个字节大小上述变量创建的特点 1. 空间开辟大小是固定的。 2. 数组在申明的时候必须指定数组的长度它所需要的内存在编译时分配。 但是对于空间的需求不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道那数组的编译时开辟空间的方式就不能满足了 int main()
{int n;scanf(%d,n);int arr[n];}上述代码只能在C99标准编译器上才行vs系列编译器均不支持那我们怎么才能在运行的时候实现上述变长数组的代码呢这时候就只能试试动态存开辟了。 动态内存函数的介绍 
malloc 函数功能开辟内存块 参数size_t:需要申请的字节数 返回值申请失败返回空指针申请成功返回指向申请该空间首地址的指针 头文件stdlib.h 注意返回指针的类型是void*,这时候需要你把该指针强制类型转化为你想要的类型这样方便访问以及解引用malloc申请来的空间是连续的但是多次malloc来的是不连续的 malloc的使用 
int main()
{int*p(int*) malloc(40);//申请了40个字节强制转化为int*类型指针if (p  NULL)//如果返回空指针的话申请失败{perror(malloc:);//打印错误信息return 1;//非正常退出}for (int i  0; i  10; i){*(p  i)  i;//对每一个四个字节大小的元素赋值这里*pi的本质就是p[i];printf(%d, *(p  i));//打印每个元素}return 0;//程序正常退出}free 功能释放内存块 参数指针接收要释放内存块的首地址 头文件stdlib.h 返回值无 了解了这些之后我们试一下释放刚才malloc来的内存块 
int main()
{int i  0;int*p(int*) malloc(40);if (p  NULL){perror(malloc:);return 1;}for (int i  0; i  10; i){*(p  i)  i;printf(%d, *(p  i));}free(p);//指针接收要释放内存块的首地址p  NULL;//很有必要否则p为野指针return 0;}当p所指向的申请的空间释放时p指针指向随机位置p变成野指针。 如果我们不释放动态内存申请的内存的时候程序结束动态申请内存由操作系统自动回收如果不用free函数释放申请好的空间就会在程序运行结束前一直存在于堆中造成内存泄漏 
int main()
{while (1){malloc(1000);}return 0;}我是不知天高地厚的年轻人哈哈哈哈哈 calloc 功能申请一个数组在内存中并且初始化为0 参数size_t num申请数组元素的个数size_t size每个元素的字节大小 返回值申请失败返回空指针申请成功返回指向申请该空间首地址的指针 头文件stdlib.h calloc函数使用 
int main()
{int i  0;int*p(int*) calloc(10,sizeof(int));//申请10个元素每个元素字节大小4if (p  NULL)//如果返回空指针的话申请失败{perror(calloc:);//打印错误信息return 1;//非正常退出}for (int i  0; i  10; i){printf(%d , *(p  i));//打印初始化的值}free(p);p  NULL;return 0;}malloc和calloc的区别与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0 我们可以看看malloc有没有先初始化 int main()
{int* p  (int*)malloc(40);//申请了40个字节强制转化为int*类型指针if (p  NULL)//如果返回空指针的话申请失败{perror(malloc:);//打印错误信息return 1;//非正常退出}for (int i  0; i  10; i){printf(%d , *(p  i));//打印每个元素}free(p);//指针接收要释放内存块的首地址p  NULL;//很有必要否则p为野指针return 0;//程序正常退出}可以看到是未初始化的放的随机值 realloc 功能内存块的扩容 参数第一个参数接收要扩容内存块的首地址扩容后总字节大小包括原来的字节大小 头文件stdlib.h 返回值  realloc函数使用 
int main()
{int* p  (int*)malloc(40);//申请了40个字节强制转化为int*类型指针if (p  NULL)//如果返回空指针的话申请失败{perror(malloc:);//打印错误信息return 1;//非正常退出}for (int i  0; i  10; i)//循环打印扩容前的元素{*(p  i)  i;printf(%d , *(p  i));}int* ptr  (int*)realloc(p, 80);//原空间够用ptrp,不够用的话ptr存放新地址if (ptr ! NULL)//扩容成功{p  ptr;//原空间够用ptrp,不够用的话ptr存放新地址重新将新地址给p}for (int i  10; i  20; i)//扩容后新空间的{*(p  i)  i;printf(%d , *(p  i));}free(p);p  NULL;return 0;
}编译运行  常见的动态内存错误 
1.对NULL指针的解引用操作 
int main()
{int* p  (int*)malloc(1000);int i  0;//if (p NULL)//{//	return 1;//}for (i  0; i  250; i){*(p  i)  i;}free(p);p  NULL;return 0;
}当malloc申请内存失败pNULL,i0,相当于给空指针解引用 解决办法对malloc函数返回值做出判断 int main()
{int* p  (int*)malloc(1000);int i  0;if (p NULL){return 1;}for (i  0; i  250; i){*(p  i)  i;}free(p);p  NULL;return 0;
}2. 对动态开辟空间越界访问 
int main()
{int* p  (int*)malloc(100);int i  0;if (p NULL){return 1;}for (i  0; i 25; i)//越界访问{*(p  i)  i;}free(p);p  NULL;return 0;
}编译运行  解决方法人为检查是否越界 修改 int main()
{int* p  (int*)malloc(100);int i  0;if (p NULL){return 1;}for (i  0; i 25; i)//25变成25{*(p  i)  i;}free(p);p  NULL;return 0;
}3.对非动态开辟内存进行free 
int main()
{int a  10;int* p  a;free(p);p  NULL;return 0;
}编译运行  解决方案你别手贱 4.使用free释放一块动态开辟内存的一部分 
int main()
{int* p  (int*)malloc(100);if (p  NULL){return 1;}int i  0;for (i  0; i  10; i){*p  i;p;}free(p);p  NULL;return 0;}编译运行  解决方案别改变p指向的地址或者用一个指针记录申请内存的首地址 plan1: int main()
{int* p  (int*)malloc(100);if (p  NULL){return 1;}int i  0;for (i  0; i  10; i){*(pi) i;printf(%d , *(p  i));}free(p);p  NULL;return 0;}plan2: int main()
{int* p  (int*)malloc(100);int* q  p;if (p  NULL){return 1;}int i  0;for (i  0; i  10; i){*p i;printf(%d , *p);p;}free(q);q  NULL;return 0;}5.多次free已经释放的内存 
int main()
{int* p  malloc(40);if (p  NULL){return 1;}free(p);free(p);p  NULL;return 0;
}编译运行  解决方案别多次free已经释放的内存滑稽 6.动态开辟内存忘记释放 
见上面 几个经典的笔试题 
1 
char *GetMemory(void)
{
char p[]  hello world;
return p;
}
void Test(void)
{
char *str  NULL;
str  GetMemory();
printf(str);
}分析定义一个char*str,让他指向空str接收GetMemory()函数返回来的地址进入GetMemory()函数return p,只能把p[]的首地址传回去而p[]是局部变量出GetMemory()p[]销毁当你传回去的时候str接收的是野地址str为野指针。打印不出来hello world 编译运行 2. 
void GetMemory(char *p)
{
p  (char *)malloc(100);
}
void Test(void)
{
char *str  NULL;
GetMemory(str);
strcpy(str, hello world);
printf(str);
}分析 
3. 
void GetMemory(char **p, int num)
{
*p  (char *)malloc(num);
}
void Test(void)
{
char *str  NULL;
GetMemory(str, 100);
strcpy(str, hello);
printf(str);
}分析 但是没有free释放内存 运行编译  4. 
void main(void)
{
char *str  (char *) malloc(100);
strcpy(str, hello);
free(str);
if(str ! NULL)
{
strcpy(str, world);
printf(str);
}
}分析 运行编译  柔性数组 也许你从来没有听说过柔性数组flexible array这个概念但是它确实是存在的。 C99 中结构中的最 后一个元素允许是未知大小的数组这就叫做『柔性数组』成员 柔性数组的特点 结构中的柔性数组成员前面必须至少一个其他成员。sizeof 返回的这种结构大小不包括柔性数组的内存。 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配并且分配的内存应该大于结构的大小以适应柔性数组的预期大小。 我们可以定义一个结构体 
struct pp {int a;int b[];};
int main()
{printf(%d, sizeof(struct pp));}编译运行  确实没有包括柔性数组大小 柔性数组的使用 
struct pp {int a;int b[];//柔性数组成员
};
int main()
{struct pp* p  (struct pp*)malloc(sizeof(struct pp)  10 * sizeof(int));//malloc中第一个元素大小柔性数组字节大小p-a  4;//赋值for (int i  0; i  10; i){p-b[i]  i;//赋值}for (int i  0; i  10; i){printf(%d , p-b[i]);//打印柔性数组}printf(%d , p-a);free(p);//free掉malloc来的空间p  NULL;//p置为空指针}我们能不能用指针代替那个柔性数组呢我们可以将指针指向的那个地方malloc来使用 定义一个结构体 
struct pp {int a;int* p;
};int main()
{struct pp* q  (struct pp*)malloc(sizeof(struct pp));//申请结构体大小的内存if (q  NULL)//判断申请是否成功{return 1;//异常退出}q-p  (int*)malloc(10*sizeof(int));if (q-p  NULL)//判断申请是否成功{return 1;//异常退出}q-a  10;//赋值for (int i  0; i  10; i){q-p[i] i;赋值}printf(%d , q-a);for (int i  0; i  10; i){printf(%d , q-p[i]);}free(q-p);//free掉p指针指向的另一块申请空间的内存q-p  NULL;//指针置空防止野指针free(q);//free掉q指向的申请的内存q  NULL;}分析 malloc过程 释放过程  编译运行  上述 代码1 和 代码2 可以完成同样的功能但是 方法1 的实现有两个好处 第一个好处是方便内存释放 如果我们的代码是在一个给别人用的函数中你在里面做了二次内存分配并把整个结构体返回给用户。用户调用free可以释放结构体但是用户并不知道这个结构体内的成员也需要free所以你不能指望用户来发现这个事。所以如果我们把结构体的内存以及其成员要的内存一次性分配好了并返回给用户一个结构体指针用户做一次free就可以把所有的内存也给释放掉。 第二个好处是这样有利于访问速度. 连续的内存有益于提高访问速度也有益于减少内存碎片,根据局部性原理连续存放的数据cup从缓冲区读取的快缓存区从内存中读取的快。 总结 
本片分享了四个动态内存函数以及常见动态内存错误几个经典的笔试题以及柔性数组的概念如果你觉得对你有帮助的话希望能留下你的点赞关注加收藏如果有不对的地方可以私信我谢谢各位佬们