C++内存分为5个区域,他们分别是堆、栈、自由存储区、静态/全局存储区、常量存储区:(1)栈:在执行函数时候,函数内部变量的存储空间都可以在栈上创建,函数执行结束时候,这些存储单元
自动的被释放,栈内部分配运算内置处理器的指令集中,效率高,但是内存分配内存容量有限。
(2)堆:就是那些由new分配的内存块,他们的释放,编译器不去管理,由我们程序员的程序控制,一般
一个new对应一个delete,如果程序员没有释放,则等到程序结束,操作系统会释放它。
(3)自由存储区:就是那些由malloc分配的内存块,它和堆非常相似,不过需要free来释放它。
(4)全局/静态变量区:全局变量和静态变量都被分配到同一块内存中。
(5)常量存储区:这是一块比较特殊的存储区,不允许被改变。
区分堆与栈:
- void func()
- {
- int *p=new int[5];
- }
这句话中:
new分配了一个堆内存(new分配堆内存),那么指针P呢?p分配了一个栈内存(因为它是一个局部变量)--->在栈内存中存放一个指向一块堆内存的指针P。堆与栈的区别:
(1)管理方式不同:对于栈来说,由编译器自动控制,而对于堆来说,释放工作要程序员自己做。
(2)空间大小不同:对于堆来讲,在32位系统下,一个堆内存差不多有4G大小,对于栈来讲,一般只有很小的空间(大约1M)
(3)能否产生碎片问题:对于堆来讲,由于不停的new/delete容易产生很多碎片,使得程序的运行速率降低,而对于栈来讲,它都是先进后出,有一定的顺序,一一对应。
(4)生长方向:对于堆来讲,生长方向向上,随着地址增大的方向生长。对于栈来讲,生长方向向下,随着地址减小的方向生长。
(5)分配方式:
堆都是动态分配,栈既有动态分配又有静态分配。
(6)分配效率:
栈的分配效率比堆高很多。
常见的内存错误及其对策:
*内存分配未成功,用户却使用了。
解决办法:在使用前用if语句判断。
*内存虽然分配成功,却未进行初始化。//别忘了初始化
1:没有初始化的观念。
2:默认为系统会将其自动初始化为0。
*内存分配了,也初始化了,但是
越界!
例如:for循环中,对于数组下标的“多1少1”
*忘记释放内存
调用一次,内存丢掉一块,到最后内存耗尽。
*
释放了内存却在使用它(1)函数的return语句,注意不要返回“栈内存”的“指针”或者“引用”,因为该内存在函数结束的时候被释放
(2)使用free或者delete语句后,未将其赋值为NULL,导致产生“野指针”
【规则一】用malloc和new申请成功后,应该立即检查指针值是否为NULL,防止指针值为NULL
【规则二】不要忘记为数组和动态内存赋初值,
防止将未被初始化的内存作为右值使用【规则三】避免数组与指针下标越界,特别当心“多1”,“少1”问题
【规则四】动态内存的申请与释放,必须配对,防止内存泄漏(即:一个malloc对应一个delete,一个new对应一个delete)
【规则五】用free或者delete释放内存后,立即将指针设置为NULL,防止产生“
野指针”
指针与数组:
数组要么在静态存储区被创建,要么在栈区被创建,数组名
对应(而不是指向)一块内存,其容量在生命期内保持不变,只有数组的内容可以改变。
指针可以随时指向任意类型的内存块,它的特征是“
可变”,我们常用指针操作动态内存。
1:修改内容
- char a[]="hello";
- a[0]='X'//数组内容可以改变
- cout<<a<<endl;
- char *p="world";//p指向常量字符串
- cout<<p<<endl;//编译器不能发现该错误,但是,常量字符串的内容不能被修改。
- -->实际输出
- Xello
- 段错误
2:计算内存容量
- char a[]="hello world";
- char *p=a;
- cout<<sizeof(a)<<endl;//12个字节
- cout<<sizeof(p)<<endl;//4个字节
sizeof可以计算数组容量,如下所示:
sizeof(a)=12,指针p指向a,但是sizeof(p)=4;这是因为sizeof(p)得到的是一个指针变量字节数,相当与sizeof(char *),而不是p所指的内存容量。(
C/C++语言没有办法知道指针所指内存容量,除非申请的时候就记住)
指针参数如何传递内存不要指望用该指针去申请动态内存
- void GetMemory(char *p,int num)
- {
- p=(char *)malloc(sizeof(char)*num);
- }
- void Test(void)
- {
- char *str=NULL;
- GetMemory(str,100);//str仍然等于NULL
- strcpy(str,"hello");//运行错误
- }
比如这个例子,编译器总是为了函数的每个参数制作临时的副本,指针参数p的副本_p,_p=p,如果函数体内的程序修改了_p的值,就导致参数p的内容做相应的改变。这就是指针可以用作输出函数的原因。
但是在本例子中,_p申请了新的内存,只是把_p所指向的内存地址改变了,p丝毫没有改变,所以函数GetMemory()并不能输出任何东西。事实上,每执行一次GetMemory()就会泄漏一部分内存,所以必须free掉。
修改一:指向指针的指针- void GetMemory(char **p,int num)
- {
- *p=(char *)malloc(sizeof(char)*num);
- }
- void Test()
- {
- char *str=NULL;
- GetMemory(&str,100);
- strcpy(str,"hello");
- free(str);
- }
修改二:函数返回- char *GetMemory(int num)
- {
- char *p=(char *)malloc(sizeof(char));
- return p;
- }
- void Test()
- {
- char *str=NULL;
- str=GetMemory(str,100);
- strcpy(str,"hello");
- printf("%s\n",str);
- free(str);
- }
-------------------用函数返回当然好,但是也有人老用错
return语句,这里需要强调“栈内存”的指针,因为该内存在函数结束的时候,自动消亡,所以不能返回。
错误一:return语句使用错误- char *GetMemory()
- {
- char p[]="hello world";//p为一个数组,分配在“栈内存上”,所以在该函数结束时候,自动消亡
- return p;//出错
- }
错误二:常量字符串,生命期内恒定不变- char *GetMemory()
- {
- char *p="hello world";
- return p;
- }
运行不会出错,但是函数GetMemory设计概念上是有错的,因为GetMemory内的“hello world”是常量字符串,位于静态存储区,它的生命期内恒定不变,无论什么时候,调用GetMemory,它返回的始终是一个“只读”的内存块。
野指针:也只真不是NULL指针,是指向“
垃圾”内存的指针。
野指针形成原因:
(1)指针变量没有被初始化,任何指针变量刚被创建的时候不会自动形成NULL指针,缺省值是随机的,它会乱指,
指针变量在创建的同时,应该被初始化为NULL。- char *p=NULL;
- p=(char *)malloc(sizeof(char)*num);
(2)指针P被free或者delete之后,没有设置为NULL,让人以为p是一个合法指针
(3)指针操作超越了变量的作用域范围
- class A
- {
- public:
- void Func()
- {
- cout<<"Func of class A"<<endl;
- }
- }
- void Test()
- {
- A *p;
- {
- A a;
- p=&a;//a为栈内存上的变量,在A *p()结束后,就释放了
- }
- p->Func();//所以p为“野指针”
- }
有了malloc/free为什么还需要new/delete? 对于非内部数据类型的对象而言,光用mallo/free无法满足动态对象的要求,
对象在创建的时候要自动执行构造函数,在对象消亡之前要自动执行析构函数。 由于
malloc/free是库函数而不是运算符,不在编译器控制范围内,不能够把执行析构函数和构造函数的任务加于mallo/free(new/delete可以自动执行构造函数/析构函数,但是malloc/free不行。
new/delete是运算符,而malloc/free是函数)
内存耗尽? 如果在申请动态内存的时候找不到足够大的内存块,malloc与new将返回NULL指针,宣告失败。
<1>判断指针是否为NULL,是则return终止函数
- void Func(void)
- {
- A *a=new A;
- if(a==NULL)
- return;
- }
<2>判断是否为NULL,立即exit
- void Func(void)
- {
- A *a=new A;
- if(a==NULL)
- exit(1);
- }
new/delete?new内置了sizeof函数,类型函数转换,安全检查功能。new在创建动态对象的同时完成了初始化工作,如果对象有多个构造函数,那么new的语句也可以有多种形式
obj *objects=new obj[100];//创建100个动态对象,不能赋初值
delete [] objects;//不能忘记 [] ,释放对象数组。
阅读(2231) | 评论(1) | 转发(1) |