C++ 内存管理,c++内存控制
C/c内存管理贴于2012-06-04 01:52 Sherwin _ Reading(1461)评论(6)编辑合集这是我第一次学习编程时整理的总结,主要来自一本叫《高质量c/c++编程》的书。很多年过去了,请再复习一遍。
1、malloc()
Malloc()函数用于分配内存:需要的总字节数作为参数传递给这个函数,返回值是指向新分配内存的指针,如果内存分配不好,返回值为NULL。
malloc()的使用技术:
some _ type *指针;
指针=malloc(count * sizeof(* pointer));
注意:
(1)这个方法保证malloc()将分配正确的内存量,而不管指针的寿命。如果指针的类型后来改变了,sizeof操作符会自动确保要分配的字节数仍然是正确的。
(2)2)malloc()返回的内存“未初始化”。这个内存可能包含任何随机垃圾。您可以立即用有效数据或至少零来初始化该内存。要用0初始化,可以使用
void*memset(void*s,intc,size _ TN);
(3)3)malloc()通过缺页异常最终获取的物理内存中的原始数据大多数情况下是0(但不能保证是0)。
2、calloc()
calloc()函数是malloc的一个简单包装器。它的主要优点是将动态分配的内存清零。
void * calloc(size _ TN member,size _ tsize);
有经验的程序员更喜欢用calloc(),因为这样新分配的内存就不会有问题。调用calloc()一定会清除0,避免调用memset()。
3、realloc()
原型:extern void * realloc(void * mem _ address,unsigned newsize);#include stdlib.h有些编译器需要# includealloc.h .将mem_address指向的内存区域大小改为newsize长度。如果重新分配成功,则返回指向已分配内存的指针,否则返回空指针NULL。当不再使用内存时,应该使用free()函数来释放内存块。
注意:此处原始内存中的数据保持不变。
4.malloc()和calloc()
malloc()和calloc()函数都可以用来动态分配内存空间,但它们略有不同:
(a)a)malloc()函数有一个参数,它是要分配的存储空间的大小:
void * malloc(size _ tsize);
calloc()函数有两个参数,即元素的数量和每个元素的大小。这两个参数的乘积就是要分配的内存空间的大小。
void*calloc(size_tnumElements,size _ tsizeOfElement);
(b)如果调用成功,函数malloc()和函数calloc()都将返回分配的内存空间的第一个地址。
(c)malloc()和calloc()的主要区别是前者不能初始化分配的内存空间,而后者可以。
(d)如果malloc()函数分配的内存空间以前从未被使用过,那么其中的每一位都可能是0;相反,如果这部分内存已经被分配,可能会有各种数据留在里面。也就是说,使用malloc()函数的程序,刚开始可以正常工作(内存空间没有被重新分配),但是过了一段时间(内存空间被重新分配),就可能出现问题。
(calloc()函数会将分配的内存空间中的每一位初始化为零,也就是说,如果你为字符型或整型的元素分配内存,那么这些元素将保证被初始化为零;如果为指针类型的元素分配内存,这些元素通常被初始化为空指针;如果为实际数据分配内存,这些元素将被初始化为浮点零。
(f)calloc(m,n)本质上等价于
p=malloc(m * n);
memset(p,0,m * n);
5、自由和删除
(a)它们只是释放指针所指向的内存,但并没有杀死指针本身。
(b)P被释放后,其地址保持不变(非空),只是这个地址对应的内存是垃圾,P就变成了“野指针”。如果此时p没有设置为NULL,人们会误以为p是合法的指针。
(c)有时候我们不记得P所指的内存是否已经释放。在继续使用P之前,我们通常使用if(p!=NULL)进行防错处理。但仅当p=NULL if(p!=NULL)才能工作。
6.new和malloc
(1)、new是C中的运算符,malloc是C中的函数.
(2) new不仅会分配内存,还会调用类的构造函数。同样,delete会调用类的析构函数,而malloc只会分配内存,不会初始化类成员。同样,free也不会调用析构函数。
(3)内存泄漏可以针对malloc或者new进行检查,不同的是new可以指示那个文件的行,但是malloc没有这个信息。
(new与malloc的效率比较
(a)new可以认为是malloc加上构造函数的执行。
(b)b)new出来的指针是直接带类型信息的,而malloc返回的所有指针都是void指针。
7.新建/删除和分配/自由
(a)New/delete是运算符,malloc/free是函数。
(b)malloc和free是C /C语言的标准库函数,new/delete是C的运算符,可以用来申请动态内存和释放内存。
(c)对于非内部数据类型的对象,单靠maloc/free无法满足动态对象的要求。构造函数在对象创建时自动执行,析构函数在对象消亡前自动执行。由于malloc/free是库函数而不是操作符,不在编译器的控制范围内,所以执行构造函数和析构函数的任务不能强加给malloc/free。
(d)不要试图用malloc/free来完成动态对象的内存管理,要用new/delete。Malloc/free和new/delete相当于内部数据类型的“对象”,因为它们没有构造和销毁的过程。
(e)C语言需要一个可以完成动态内存分配和初始化的操作符new和一个可以完成内存清理和释放的操作符delete。注意,new/delete不是库函数。
(f)newdelete在实现中实际调用malloc,free函数。除了分配内存,newoperator调用构造函数,malloc函数只负责分配内存。
(g)既然new/delete的函数完全覆盖了malloc/free,为什么C不消除malloc/free?这是因为C程序经常调用C函数,而C程序只能用malloc/free管理动态内存。如果“new创建的动态对象”是免费发布的,对象无法执行析构函数,可能会导致程序错误。如果用delete来释放“malloc应用的动态内存”,理论上程序不会出错,但是程序的可读性很差。所以new/delete必须成对使用,malloc/free也一样。
8.使用malloc/free积分
(a)malloc只关心两个元素:“类型转换”和“sizeof”
(b)b)malloc函数本身并不识别要申请的内存类型,它只关心内存的总字节数。
(c)malloc返回值的类型是void*,所以在调用malloc时,需要显式转换类型,将void*转换成需要的指针类型。
(d)statement free(p)之所以能正确释放内存,是因为事先知道指针p的类型和它所引用的内存的容量。如果p是空指针,那么无论free对p操作多少次,都不会有问题。如果p不是空指针,那么对p连续两次free操作都会导致程序运行不正确。
9.使用新建/删除的要点
(a)运算符new比函数malloc使用起来简单得多。这是因为new内置了sizeof、类型转换、类型安全检查等功能。对于具有非内部数据类型的对象,new在创建动态对象时完成初始化。如果一个对象有多个构造函数,new语句也可以有多种形式。
例如:
int * P1=(int *)malloc(sizeof(int)* length);
int * p2=new int[length];
(b)如果对象数组是用new创建的,那么只能使用对象的无参数构造函数。
obj * objects=new obj[100];//创建100个动态对象
(c)使用delete释放对象数组时,注意不要丢失符号“[]”。删除[]个对象;//正确用法
10.野生指针
3354“野指针”不是空指针,而是指向“垃圾”内存的指针。一般人不会误用空指针,因为用if语句很容易判断。但是“野指针”非常危险,if语句对它不起作用。
“野指针”主要有两个原因:
(a)指针变量未初始化。当创建任何指针变量时,它不会自动变成空指针。它的缺省值是随机的,它将是任意的。因此,指针变量应该在创建时初始化,要么将指针设置为NULL,要么指向合法内存。
(b)指针P被释放或删除后没有设置为NULL,让人误以为P是合法指针。
(c)指针操作超出了变量的范围。这种情况很难避免,
1.内存分配方法
有三种分配内存的方法:
(a)从静态存储区分配。内存在程序编译时就已经分配好了,在程序的整个运行期内存都是存在的。比如全局变量,静态变量。
(b)在堆栈上创建。函数执行时,可以在栈上创建函数中局部变量的存储单元,这些存储单元在函数执行结束时自动释放。堆栈内存分配内置在处理器的指令集中,效率非常高,但分配的内存容量有限。
(c)从堆中分配,也称为动态内存分配。程序运行时,用malloc或new申请任意数量的内存,程序员负责什么时候用free或delete释放内存。动态记忆的寿命是由我们决定的。它使用起来非常灵活,但也有最多的问题。
* *常见的内存错误及其对策
1.内存分配不成功,但已被使用。
新手程序员经常犯这个错误,因为他们没有意识到内存分配会不成功。常见的解决方法是在使用内存之前检查指针是否为空。如果指针p是函数的参数,使用assert(p!=NULL)进行检查。如果malloc或new用于申请内存,则为if(p==NULL)或if(p!=NULL)进行防错处理。
2.虽然内存分配是成功的,但是在初始化之前引用它。
造成这个错误的原因主要有两个:一是没有初始化的概念;二是误以为内存默认初始值都是零,导致引用初始值(比如数组)的错误。
内存的默认初始值是什么,没有统一的标准,虽然有时候是零,但我们宁愿相信一切都是可信的。所以不管你怎么创建数组,别忘了赋初值,连零值都不能省略。不用麻烦了。
3.内存分配成功并已初始化,但操作超出了内存边界。
例如,下标“大于1”或“小于1”在使用数组时经常出现。尤其是在for循环语句中,循环次数容易弄错,导致数组运算越界。
4.忘记释放内存,导致内存泄漏。
包含此错误的函数每次被调用时都会丢失一段内存。一开始,系统有足够的内存,所以你看不到错误。最后程序突然死机,系统显示提示:内存耗尽。
动态内存的申请和释放必须成对,malloc和free在程序中的使用次数必须相同,否则必然出错(new/delete也是如此)。
5.释放内存但继续使用。(有三种情况:)
(1)程序中的对象调用关系太复杂,确实很难知道一个对象是否释放了内存。这时候就要重新设计数据结构,从根本上解决对象管理混乱的局面。
(2)函数的返回语句错误。注意不要将“指针”或“引用”返回到“堆栈内存”中,因为这个内存在函数体结束时会被自动销毁。
(3)使用free或delete释放内存后,指针不设置为空。导致“野指针”。
* *纠正预防措施:
(a)用malloc或new申请内存后,应该立即检查指针值是否为空。防止使用指针值为空的内存。
(b)不要忘记给数组和动态内存分配初始值。防止未初始化的内存被用作正确的值。
(c)避免数组或指针的越界下标,特别要当心“多1”或“少1”的操作。
(d)动态内存的应用和释放必须成对进行,以防止内存泄漏。
(f)使用free或delete释放内存后,立即将指针设置为空,以防止“野指针”。
12.指针和数组的比较
(a)指针和数组在很多地方可以互换使用,但并不等价!
(b)数组或者在静态存储区域(例如全局数组)中创建,或者在堆栈上创建。数组的名字对应(而不是指向)一块内存,它的地址和容量在生存期内保持不变,只有数组的内容可以改变。
(c)指针可以在任何时候指向任何类型的内存块。它的特点是“易变”,所以我们经常用指针来操作动态内存。指针远比数组灵活,但也更危险。
(d)如果要将数组A的内容复制到数组B,不能使用语句b=a,而应该使用标准库函数strcpy进行复制。
(f)比较B和A的含量是否相同。不能用if(b==a)来判断,而要用标准库函数strcmp来比较。
(g)语句p=a不是把A的内容复制到指针P,而是把A的地址赋给P,可以用库函数malloc为P申请一个strlen(a) 1字符的内存,然后用strcpy复制字符串。
(h)语句if(p==a)比较的不是内容而是地址。应该和库函数strcmp比较一下。
(I)数组的容量(字节数)可以通过使用操作符sizeof(注意不要忘记 \0 )来计算。
(j)C /C语言没有办法知道指针所指的内存容量,除非在申请内存的时候记住。
(k)注意,当一个数组作为函数的参数传递时,该数组会自动退化为相同类型的指针。(即函数中数组名的本质是对应的指针)。
13.指针参数的转移存储器
(a)如果函数的参数是指针,则指针不能用于申请动态内存。
(b)如果你必须使用指针参数来申请内存,你应该使用指针对指针来代替。
void GetMemory2(char **p,int num)
* p=(char *)malloc(sizeof(char)* num);
无效测试2(无效)
char * str=NULL
GetMemory2( str,100);//注意参数是str,不是str
strcpy(str, hello );
cout str endl
免费(str);
}
(c)由于“指针对指针”的概念不容易理解,我们可以用函数返回值来传递动态内存。这个方法比较简单。
char *GetMemory3(int num)
char * p=(char *)malloc(sizeof(char)* num);
返回p;
无效测试3(无效)
char * str=NULL
str=get memory 3(100);
strcpy(str, hello );
cout str endl
免费(str);
}
14.应对记忆衰竭。
如果在申请动态内存时找不到足够大的内存块,malloc和new会返回一个空指针,声明内存申请失败。通常有三种方法来处理“内存耗尽”问题。
(a)确定指针是否为空,如果是,立即用return语句终止该函数。
无效函数
A *a=新A;
if(a==NULL)
返回;
}
(2)判断指针是否为空,如果是,立即用exit(1)终止整个程序。例如:
无效函数
A *a=新A;
if(a==NULL)
cout“内存耗尽”endl
出口(1);
}
(3)为new和malloc设置异常处理函数。例如,VisualC可以使用_ set _ new _ handler函数为new设置用户定义的异常处理程序,或者malloc可以享受与new相同的异常处理程序。详情请参考C用户手册。
**(1)(2)模式是最常用的。如果一个函数中有多个地方需要申请动态内存,那么方法(1)是不够的(释放内存比较麻烦),应该用方法(2)来处理。
江西理工大学理学院舍温整理
2009-2-1723:27
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。