(超详解)---->> 动态内存管理

news/2024/10/15 14:14:20/

目录

本章重点

        1:malloc,calloc,realloc,free 函数

        2:常见动态内存错误

        3:几道经典的笔试题


1:malloc,calloc,realloc,free函数

        首先我们平时所创建的变量数组都是在栈区上开辟的内存,空间开辟的大小固定的。

        而有些程序是要在运行的时候它所需要的空间才能知道。

        这4个函数在内存中使用的时候是在堆区开辟空间的。

        malloc:使用语法是malloc(size_byte);,size_byte指的是需要开辟多少字节空间的内存,malloc函数的返回值开辟空间的地址类型为void*,所以在使用的时候我们需要根据需求来强制类型转换

        malloc开辟空间的时候可能不会成功的开辟,如果失败的话则返回NULL,所以一般在使用完malloc开辟的空间之后我们会对地址来进行判断,这样可以及时提醒我们是否开辟空间成功。

malloc对于开辟的空间的内容不会初始化,且最后我们需要借助free函数释放我们所开辟的空间。对于malloc(0)这个行为是未定义的。

        free:使用语法是free(ptr),这里的ptr是一个指针,返回值为void,函数参数为void*的指针,它是用来释放动态内存开辟的空间,对于非动态内存开辟的空间如果用free来释放,那么程序可能会直接报错,如果对与NULL指针释放,及free(NULL),这时我们的free啥都不会做

        calloc:使用语法为calloc(num,size),num表示要开辟多少个数量size表示每个空间的大小,单位是字节,函数功能是开辟num个大小为size的空间开辟成功返回值为开辟空间的地址,开辟失败返回值为NULL与malloc最主要的区别就是它开辟完空间之后会将每个字节的空间大小全部初始化为0如下图:>

 所有当我们到底使用malloc还是calloc我们可以看我们是否需要初始化空间。

realloc:它是用来调整我们动态内存开辟空间大小的,有时我们开辟的空间过小,也有时我们开辟的空间过大,我们都可以用realloc来调整我们动态内存空间的大小,所有realloc函数的出现使我们

更加合理的来动态内存管理

        

realloc函数的原型
void* realloc (void* ptr, size_t size);ptr:代表我们所需要调整空间的地址
size:表示我们需要重新调整空间的大小,单位是字节

        当realloc需要扩容的时候,会出现两种情况:>

        情况1:当realloc所开辟的空间的大小在原空间后面有足够大的空间,那么realloc的返回值还是需要调整空间的地址

 

情况2:原空间后面没有多余的空间,则在这时我们的realloc函数会在堆区寻找另外一块足够大的空间去申请一块空间,这时realloc会自动将原空间的内容拷贝到新开辟的空间处,并且realloc函数会自动释放原来地址的空间,然后返回新的空间的起始地址

 

 

2:常见的动态内存错误

常见错误1:对NULL解引用操作

void test()
{int *p = (int *)malloc(INT_MAX/4);*p = 20;free(p);
}
int main()
{test();   return 0;
}

解释:因为INT_MAX,是整形取值的最大值,而用malloc去开辟空间的时候可能会失败,所以p可能是NULL,此时*p则代表对空指针进行解引用,此时属于非法访问内存了。

解决办法:对于刚开辟出来的空间我们就对其进行判断,并且如果申请空间失败就直接退出程序。

常见错误2:越界访问内存

void test()
{int i = 0;int *p = (int *)malloc(10*sizeof(int));if(NULL == p){exit(EXIT_FAILURE);}for(i=0; i<=10; i++){*(p+i) = i;//当i是10的时候越界访问}free(p);
}

解释:我们知道p是一个(int*)类型的指针,p+i则代表跳过i个整形而在此处的代码当中,当i等于10的时候,我们p跳过10个整形刚好跳过我们申请的空间,而此时在对p进行解引用操作,则算属于非法访问内存,程序会崩溃的。

图解:

解决办法:对于开辟出来空间的大小我们要牢记,防止非法访问其他内存的空间。

常见错误3:对非动态开辟的空间进行释放

void test()
{int a = 10;int *p = &a;free(p);//ok?}

 这里的a是在栈区开辟的空间,而free函数是对动态内存开辟的空间进行释放的。所以这个程序是error的

解决办法:牢记free函数的作用,是对动态开辟的内存空间进行释放的。

常见错误4:释放一部分动态内存空间

void test()
{int *p = (int *)malloc(100);p++;free(p);//p不再指向动态内存的起始位置}

解释:在这里p是开辟的空间的地址,且又是整形指针所以+1则代表跳过1个整形的地址,所以这里的p不在是指向内存的首地址了。所以只对申请空间的一部分进行了释放,还是会存在内存泄漏的,所以这也是一个错误。

解决办法:对于开辟出来的空间的地址我们不要进行移动。

常见错误5:对同一块内存空间进行多次释放

void test()
{int*p=(int*)malloc(100);free(p);free(p);}

 解释:在这里对p进行了多次释放,第一次释放是正确的是对自己申请的空间进行释放,而第二次释放的时候就是错误的了,因为第二次的释放是对空间的非法访问。所以也是error的,当然在说这些前提下我们都认为我们申请的空间是有效的。

解决办法:开辟一次,释放一次。

常见错误6:对动态内存开辟的空间未释放,形成内存泄漏

void test()
{int* p =(int*)malloc(100);if(p!=NULL){*p=20;}}int main()
{test();while(1);return 0;
}

解释:当我们调用test函数结束后,因为p是一个局部变量是在栈区上开辟的,所以出了test函数的作用域后,p就会销毁,而我们也为对开辟出来的空间进行释放,所以我们就会浪费这100个字节的空间,所以就会形成内存泄漏的问题。

解决办法:当我们开辟出来的空间没有进行使用得时候我们一定要记得释放这一块空间。

3:经典笔试题

题目1:

void GetMemory(char* p)
{p=(char*)malloc(100);
}void Test(void){char*  str =NULL;GetMemory(str);strcpy(str,"hello world");printf(str);
}

解释:首先这个程序是错误的,因为对NULL进行了非法访问,str一开始是NULL,GetMemory(str),想法是给str开辟一块空间,可是我们要记住,p是一个形式参数,会独自开辟内存空间,对形式参数的影响不会影响实参。且该函数还存在内存泄漏的问题

 题目2:

char* GetMemory(void)
{char p[] ="hello world";return p;
}
void Test(void)
{char* str =NULL;str =GetMemory();printf(str);
}

解释:在GetMemory函数当中,p是一个数组,p是指向"hello world",出函数就会销毁,而此时将p的值给str,尽管此时我们的str还是指向曾今的那块空间的地址,可是出了函数范围的时候,这块空间就会还给操作系统,所以此时的str是一个野指针打印出来的就是烫烫烫...属于返回栈空间地址的问题。

题目3:

void GetMemory(char** p, int num)
{*p = (char*)malloc(num);
}void test()
{char* str = NULL;GetMemory(&str, 100);strcpy(str, "hello world");printf(str);
}
int main()
{test();return 0;
}

解释:这个程序可以打印出来结果,就是当我们使用完开辟的空间后未进行内存的释放,会形成内存泄漏的问题。

题目4:

void Test()
{char* str = (char*)malloc(100);strcpy(str, "hello");free(str);if (str != NULL){strcpy(str, "world");printf(str);}}

解释:这段代码虽然可以在vs中打印出来,但是这段代码还是有问题的。

       因为我们已近对str这块空间先释放的,所以已近将这块空间还给操作系统了,而此时str还是指向那块空间的地址,所以world还是会被拷贝进去。但是这已近形成非法访问内存了。所以我们在日常敲代码的过程中如果已近将某一块空间给释放了,我们一定要将指针置为空,这样我们才不会形成非法访问内存的可能。

                                感谢大家的观看!


http://www.ppmy.cn/news/887239.html

相关文章

spring cloud 之 Hystrix

Hystrix概述 Hystrix是一个供分布式系统使用&#xff0c;提供延迟和容错功能&#xff0c;保证复杂的分布系统在面临不可避免的失败是时&#xff0c;仍具有弹性。 当服务器A调用服务器B时&#xff0c;如果服务器B宕机&#xff0c;则服务器A不去调用。当服务器B在时间范围内未响…

输入年月日判断是一年里的第几天

import java.util.Scanner;public class K612 {public static void main(String[] args) {Scanner anew Scanner(System.in);//判断是一年里的第几天int days0;System.out.println("输入年");int yeara.nextInt();System.out.println("输入月");int mounth…

常用UAPROFILE下载地址

If you’re interested in the capabilities of a particular device, I have assembled a list of all the UAProf URLs from the WURFL database: AirnessAir99 http://www.airnessmobile.com/uaprof/Airn… slide99 http://www.airnessmobile.com/uaprof/slid… AlcatelMand…

CENTREX业务简介

CENTREX业务简介 市内电话业务作业要点称为集中式数位小交换机 (CENTRalized EXchange,简称CENTREX,又称虚拟总机),系利用软体将用户专用交换机(俗称总机,PBX)之功能内建在局用市话交换机上,直接由局用交换机提供PBX服务功能.亦即由局用交换机扮演虚拟PBX之角色,CENTREX群之分机…

matlab 曲线数据输出,Mathlab 如何输出曲线各数据点值?

编了个程序,得到了预期的多条x-y曲线,但是想得到各时间点的具体值。研究了好几天,还是做不出。 哪位高手能演示下? 随便输出哪一条曲线都可以。 程序如下: function IV tspan=[0, 30]; x0 = [0 0 0 0 0 100 0 0 0 0 0 0 0 0 0 0 100]; [T, X] = ode45(@lun_fun, tspan,…

LabVIEW开发图像采集和图像处理程序

LabVIEW开发图像采集和图像处理程序 扫描电子显微镜&#xff08;SEM&#xff09;是一种功能强大的工具&#xff0c;广泛用于高分辨率的生物和半导体样品检测。然而&#xff0c;对于大面积或3D成像&#xff0c;SEM成像是一个耗时的过程。MBSEM旨在通过同时扫描多个像素来减少采…

PyTorch翻译官网教程7-OPTIMIZING MODEL PARAMETERS

官网链接 Optimizing Model Parameters — PyTorch Tutorials 2.0.1cu117 documentation 优化模型参数 现在我们有了一个模型和数据&#xff0c;是时候通过优化我们的数据参数来训练、验证和测试我们的模型了。训练模型是一个迭代过程;在每次迭代中&#xff0c;模型对输出进…

计算机主机检测不到耳机,win10电脑检测不到耳机怎么办_win10电脑检测不到耳机如何解决-系统城...

日常在使用win10电脑听歌曲时喜欢插入耳机&#xff0c;这样音质比直接外放好多了&#xff0c;最近很多win10用户出现了耳机拔下来再插上电脑然后耳机听不到声音&#xff0c;检测耳机是没问题的&#xff0c;那么就是设置出现问题&#xff0c;针对此故障&#xff0c;小编这里和大…