笔试面试那件小事(内存管理)

1->内存分配方式:

内存分配方式有三种:

a):从静态存储区分配。例如程序中定义的全局变量和static变量就是这种方式分配,内存在编译的时候已经分配好了,这块内存在程序的整个运行期间都存在。

b):在栈上创建。这种情况程序的是最多的,例如我们再程序中定义的int var就是这种情况的内存分配方式。在执行函数的过程中,函数内的局部变量的存储单元就是在栈上创建的,在函数执行结束时,这些存储单元自动被释放。

c):从堆上分配,亦称为动态内存分配。程序在运行的时候会用到malloc或new申请任意多少的内存,程序员自己负责在何时用free或者delete释放内存。动态内存的生存期由我们自己决定,在使用上相对比较灵活。

 

2->常见的内存分配错误和处理方法:

a):内存分配未成功,却使用了它(在使用内存之前检查其是否为空)。如果指针p是函数的参数,那么在函数的入口处使用assert(p!=NULL)进行检查。如果采用malloc分配的内存,加上判断语句if(p==NULL)或者if(p!=NULL)进行防错处理

b):内存分配虽然成功,但是尚未初始化就引用它。例如:未初始化的数组误以为它里面存放的数据都为0

c):操作越界

d):忘记释放内存

e):释放了内存却继续使用它。一般有三种情况:程序的调用过程过于复杂,很难理清哪个动态内存已经释放。函数的return语句返回局部变量的指针或者引用。使用了free或者delete释放内存之后,没有将指针置为NULL。导致产生了野指针。

 

3->指针和数组

数组要么在静态内存区被创建(例如全局数组),要么在栈上被创建,数组名对应(而不是指向)一块内存,其地址在生命周期内保持不变,可以改变的是数组内的内容。

指针可以随时指向任意类型的内存块,它的特征就是“可变”,我们常常用指针来操作内存。

a):关于二者的内容修改

Code:

char a[]="hello";

a[0]='X';  cout<<a<<endl;

char *p="World";     //P指向常量字符串    p[0]='X';//试图修改常量字符串的内容,但是编译器发现不了这样的错误

cout<<p<<endl;

解释:字符数组a的容量是6个字符,其内容为“hello\0”(在栈上),a的内容是可以改变的。而指针指向的是常量字符串“world”(位于静态存储区)内容是“world\0”,常量字符串的内容是不可以被修改的。p[0]='X',试图修改常量字符串的内容而导致运行错误。

b):关于两者进行内容复制

不能对数组名进行直接复制与比较,若想把数组a的内容复制给数组b,不能用语句b=a,应该采用标准库函数strcpy进行。同样要比较数组a和数组b中的内容是否相同,不可以直接用if(a==b)进行判断,要用库函数strcmp进行比较。

语句p=a并不是把a的内容复制给p,而是将a的地址赋值给p。要想复制a的内容,可以先用malloc函数为p分配一个strlen(a)+1字符串的内存,再调用strcpy函数进行字符串的复制。同样指针的比较不是内容的比较而是地址的比较。

c):关于两者内存容量的计算

用运算符sizeof可以计算出数组的容量(字节数)。例如a[]="hello world";sizeof(a)的值是12,(\0也要计算在内),而对于指向a的指针p,sizeof(p)的值却是4,这时候得到的是一个指针变量的字节数。相当于计算sizeof(char*),而不是p所指向的内存容量。

另外当数组作为函数的实参进行传递时,数组退化为指针。

 

4->指针传递内存

如果函数的参数是一个指针,不要指望该指针去申请动态内存。例如,Test函数的语句并没法获得Getmemory(str,200)动态分配的你才,str仍然指向NULL

void GetMemory(char *p,int num){

p=(char *)malloc(sizeof(char)*num)

}

void Test(){

char *str=NULL;

GetMemory(str,100);           //str依然为空

strcpy(str,"hello");   //运行错误

}

解释:编译器总是要为每个参数制作临时副本,指针参数p的副本_p,编译器是_p=p.如果函数体内的程序修改了_p的内容将导致参数p的内容作了相应的修改。但是在本例中_p申请了新的内存,只是把_p所指的内存地址改变了,没有改变p所指的内存。所以p还是为NULL。

如果一定要用指针去申请内存,那么就采用指针的指针,将上述代码做如下的修改:

void GetMemory(char **p,int num){

*p=(char*)malloc(num*sieof(char));

}

void Test2(){

char *str=NULL;

GetMemory(&str,100);

strcpy(str,"hello");

cout<<str<<endl;

free(str);

}

同样我们也可以用return返回动态分配的内存:

char * GetMemory(int num){

char *p=(char*)malloc(sizeof(char)*num);

return p;

}

这时候要特别注意,不能返回栈内存的指针。

char *GetString(){

char p[]="hello word";

return p;                                 //这个是完全错误的程序

}

 

5->关于free和delete

free和delete只是把指针所值的内存给释放掉,但是还保留指针本身。如果不把指针设置为NULL,就会成为野指针。

 

6->动态内存会随着程序结束自动释放吗?

答案是不会的。这个知识涉及到了两个理论:指针消亡了,并不代表它所指向的内存会被自动释放。内存释放了并不代表了指针会消亡或者为NULL、

 

7->野指针

”野指针“不是NULL指针,是指向”垃圾“内存的指针。

形成野指针的原因:指针变量没有初始化,任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值一般是随机的,所以在指针变量被创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。

另外指针p被free或者delete之后,没有置空,同样也会使指针p成为野指针。还有一种就是指针越界。

 

8->malloc/free和new/delete

malloc与free是C++/C 语言的标准库函数,new/delete是C++的运算符,它们都可用于申请动态内存和释放内存。对于非内部数据类型的对象而言,光用malloc/free无法满足动态对象的要求。对象在创建的同时要自动调用对象的构造函数,对象消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器的控制范围之类,不能把构造函数和析构函数的任务强加于malloc/free.

因此C++语言需要一个能完成动态内存分配和初始化的工作的运算符new,以及能够完成清理与释放内存工作的运算符delete。

 

9->关于内存耗尽的问题

如果在申请动态内存时找不到足够大的内存块,malloc和new将返回NULL指针,宣告内存申请失败,就是通常所说的内存耗尽问题。

一般的处理方法就是判断申请的地址是否为NULL。

 

10->再看看malloc/free

malloc的函数原型:void *malloc(size_t size);

申请一个长度为length的整数类型的内存:int *p=(int *)malloc(sizeof(int)*lenght);

注意:malloc返回值类型是void*,所以在调用malloc时要显示地进行类型转换,将void* 转化成所需要的内存类型

另外malloc函数本事不识别要申请的内存是什么类型,它只关心内存的总字节数。

free的函数原型:void free(void *memblock);

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值