资讯动态

动态内存管理续(c++方向必看)

发布时间:2026/10/5 10:27:23 来源:尧图企业网站定制
引言上一篇文章主要介绍了动态内存的函数和基本用法。https://blog.csdn.net/2301_81479880/article/details/166902312?fromshareblogdetailsharetypeblogdetailsharerId166902312sharereferPCsharesource2301_81479880sharefromfrom_link本文主要是运用动态内存开辟的一些注意事项典型错误经典例题和柔性数组。学好动态内存管理对于后续的学习数据结构是很有帮助的。目录1.常见的动态内存错误1.1对NULL指针的解引用操作1.2对动态内存空间的越界访问1.3对非动态开辟的内存使用free1.4使用free释放一块动态内存开辟的一部分1.5对同一块动态内存多次释放1.6动态开辟内存忘记释放内存泄漏2.动态内存管理经典笔试题2.1第一题2.2第二题2.3第三题3.柔性数组3.1柔性数组的特点3.2柔性数组的使用3.3柔性数组的优势1.常见的动态内存错误1.1对NULL指针的解引用操作void test() { int* p (int*)malloc(INT_MAX / 4); *p 20;//如果pd的值是NULL就会出问题 free(p); }使用malloc开辟内存失败时返回值为NULL如果不加判读直接使用程序就会崩溃所以在使用完malloc后及时判断如果p不为空程序才能继续使用。1.2对动态内存空间的越界访问void test() { int i 0; int* p (int*)malloc(10 * sizeof(int)); if (NULL p) { return 1; } for (i 0; i 10; i) { *(p i) i;//当i为10时越界访问 } free(p); }动态开辟的空间也是有自己使用范围的超出这个范围就会越界访问程序错误。1.3对非动态开辟的内存使用freevoid test() { int a 10;///局部变量创建在栈区上 int* p a; free(p); }如果我们不信邪头铁尝试一下程序直接就崩溃了我们上一篇文章讲了free函数用来释放动态开辟的内存但是局部变量int是创建在栈区强行使用free释放程序就会崩溃。1.4使用free释放一块动态内存开辟的一部分int main() { int* p (int*)calloc(5, sizeof(int)); if (p NULL)//开辟失败时返回NULL时 { perror(use malloc);//打印错误信息 return 1; } //使用空间 int i 0; for (i 0; i 5; i) { *p i 1; p; } //释放内存 free(p);//这里的p不再是起始位置 p NULL; return 0; }程序执行完后p指向的是下标4或者是指向中间位置这个时候使用free5个字节程序也会崩溃所以释放空间时一定要让p指向起始位置。1.5对同一块动态内存多次释放void test() { int* p (int*)malloc(100); free(p); //解决办法 p NULL把p置为空 free(p);//重复释放 }同一块空间只能释放一次吗重复释放程序就会报错如果我们养好良好的代码习惯每次释放完空间后把p置NULL后面的free就不会生效了。1.6动态开辟内存忘记释放内存泄漏void test() { int* p (int*)malloc(100);//开辟了100字节的空间 if (NULL ! p) { *p 20; } } int main() { test(); while (1);//死循环 return 0; }当动态开辟的空间忘记释放时别人是无法使用这块空间的这就造成了内存泄露注意动态开辟的空间一定要以正确的方法释放如果不释放也要交代清楚你在代码中进行了动态内存分配记得释放。小结1.动态申请的内存空间可以使用free直接释放。2.如果没有使用free释放在程序结束时这个内存也会被操作系统回收。2.动态内存管理经典笔试题2.1第一题#include stdio.h #include stdlib.h #include string.h 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(); return 0; }这个代码能运行吗我们可以看到程序的返回值不是0说明程序在运行中就崩溃了。解析C 语言只有值传递。调用GetMemory(str)时传给形参p的是str的一份拷贝p和str是两个不同的变量。所以strcpy(NULL, hello world)就是往地址 0 写数据属于未定义行为通常表现为段错误。顺带一提那块 100 字节的内存在函数返回后就没人持有它的地址了同时造成内存泄漏。注意hello world连结尾\0共 12 字节100 字节本身够用这里不是越界问题而是指针根本没被赋值。2.2第二题char* GetMemory(void) { char p[] hello world; return p; } void Test(void) { char* str NULL; str GetMemory(); printf(str); } int main() { Test(); return 0; }这个代码正确吗运行结果是什么解析创建char*类型的str并初始化为NULLGetMemory函数中返回了p的地址假设为0x12ff48由str接收现在str中存放的就是0x12ff48可以通过地址打印数组hello world。看起来没有问题但是数组p是局部变量它的生命周期只在GetMemory函数中出了函数它就被释放掉了所以str接收的其实是一个已经释放掉的空间此时它就成为了野指针强行打印就是非法访问结果是一堆乱码。2.3第三题#include stdio.h #include stdlib.h #include string.h void Test(void) { char* str (char*)malloc(100); strcpy(str, hello); free(str); if (str ! NULL) { strcpy(str, world); printf(str); } } int main() { Test(); return 0; }解析经典错误free了之后再次使用动态开辟的空间ree(str)释放的是那块堆内存str这个变量本身不受任何影响它依然保存着原来的地址所以if (str ! NULL)永远为真这个判断毫无意义反而给了人一种已经检查过、很安全的错觉。随后往已经回收的空间内写数据如果这块空间已经分配出去了会破坏别人的数据但也有可能还没有分配出去程序正常跑完。当我们free完成后将str置为NULL程序就没有问题了。3.柔性数组在c99中结构体的最后一个元素允许是未知大小的数组这就叫做柔性数组成员。struct st_type { int i; int a[]; //int a[];有些编译器不支持这种写法 }3.1柔性数组的特点1.结构中的柔性数组成员前面必须至少⼀个其他成员。2.sizeof返回的这种结构大小不包括柔性数组的内存。3. 包含柔性数组成员的结构⽤malloc()函数进性内存的动态分配并且分配的内存应该大于结构的大小以适应柔性数组的预期大小。3.2柔性数组的使用struct S { int n; int arr[];//柔性数组成员我希望arr开始的时候能存放5个整行后期arr的空间大小可调整 }; int main() { struct S* ps (struct S*)malloc(sizeof(struct S) 5 * sizeof(int));//24 //创建一个结构体指针来接收 if (ps NULL) { perror(use malloc); return 1; } ps-n 100; int i 0; for (i 0; i 5; i) { ps-arr[i] i1; } //扩容 struct S* ptr (struct S*)realloc(ps, sizeof(struct S) 10 * sizeof(int)); if (ptr ! NULL) { //扩容失败 perror(use realloc); } else { //扩容成功 ps ptr; ptr NULL; for (i 5; i 10; i) { ps-arr[i] i 1; } } //释放 free(ps); ps NULL; return 0; }当我们的空间大小需要调整时通过柔性数组就可以做到。3.3柔性数组的优势struct S { int n; int *arr; }; int main() { struct S* ps (struct S*)malloc(sizeof(struct S)); if (ps NULL) { perror(use malloc); return 1; } ps-n 100; int* ptr (int*)malloc(5 * sizeof(int)); if (ptr ! NULL) { ps-arr ptr; } else { perror(use malloc); return 1; } int i 0; for (i 0; i 5; i) { ps-arr[i] i1; } //扩容 struct S* ptr2 (struct S*)realloc(ps-arr, 10 * sizeof(int)); if (ptr2 ! NULL) { //扩容失败 perror(use realloc); } else { //扩容成功 ps-arr ptr2; ptr2 NULL; for (i 5; i 10; i) { ps-arr[i] i 1; } } //释放 free(ps-arr); ps-arr NULL; free(ps); ps NULL; return 0; }这里使用结构体包含指针的方式也能完成但是柔性数组有两个优势。1.方便内存释放如果我们的代码是在一个给别人用的函数中你进行了二次分配内存并且把整个结构体返还给用户用户可以通过free释放结构体但是如果用户不知道结构体内部的成员也要free就会造成内存泄漏。所以我们把结构体的内存和成员变量要的内存一次性分配好然后返回一个结构体指针这样只要做一次free就可以把所有的内存释放。2.有利于提高访问速度连续的内存有益于提⾼访问速度也有益于减少内存碎⽚。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑