一直都把堆栈放一起,所以很多人会误以为他们的组合是一个词语,就像“衣服”一样简单,其实不然,今天在下就将最近学习总结的一些与大家分享。
一个由C/C++编译的程序占用的内存分为以下几个部分:
1、栈区(stack):又编译器自动分配释放,存放函数的参数值,局部变量的值等,其操作方式类似于数据结构的栈。
2、堆区(heap):一般是由程序员分配释放,若程序员不释放的话,程序结束时可能由OS回收,值得注意的是他与数据结构的堆是两回事,分配方式倒是类似于数据结构的链表。
3、全局区(static):全局变量和静态变量被分配到同一块内存中,C语言中区分初始化和未初始化的,C++中不再区分了。
4、文字常量区:常量字符串就是放在这里,程序结束后由系统释放。
5、程序代码区:存放函数体的二进制代码。
*Data Segment中存放已初始化的全局或静态变量。
*BSS中存放未初始化的全局或静态变量。
malloc()动态开辟的从堆里获取空间。就是说malloc函数返回的指针是指向堆里的一块内存。操作系统中有一个记录空闲内存地址的链表,当操作系统收到程序的申请时,就会遍历链表。当使用 malloc() 分配过大的空间,比如超出 0x20ff8 这个常数(在我的系统(Fedora15)上是这样,别的系统可能会有变)时,malloc 不再从堆中分配空间,而是使用 mmap() 这个系统调用从映射区寻找可用的内存空间。如果你在函数上面定义了一个指针变量,然后在这个函数里申请了一块动态分配的内存让指针指向它。实际上,这个指针的地址是在栈上,但是它所指向的内容却是在堆上面。这一点要注意!所以,在一个函数里申请了空间后,比如:
void Function(void) { char *p = (char *)malloc(100 * sizeof(char)); }
不要认为上面的函数返回,函数所在的栈被销毁指针也跟着销毁,申请的内存也就一样跟着销毁了。这是错误的。因为申请的内存在堆上,除非程序员手动释放,否则要等到程序结束释放所有内存才会释放它们,跟函数是否结束没有关系。也就是函数所在的栈被销毁跟堆完全没有关系。所以,忠告就是:使用完不再需要记得释放动态分配的内存!在该部分程序退出之前释放内存并立即给P赋0值(NULL)。另一个办法是保证P在没有初始化之前,将不再被使用。在函数中的new也是同样的,函数结束后并不会自动释放。
C++中
new
new运算返回所分配内存单元的起始地址,所以需要把返回值保存在一个指针变量中。若分配不成功,返回NULL,并抛出异常。
new没有为创建的对象命名,只能通过指针去访问对象或者数组。
delete
delete <指针变量>
delete []<动态分配的数组名>
new和delete必须配对使用。
虽然程序结束后系统会自动释放程序和其中数据所占的内存空间,但是为了在程序运行过程中能够重复使用有限的内存资源,防止系统产生内存泄漏,还是应该即时释放不需要的动态分配的内存单元,以便系统能随时对该内存单元进行分配。
delete释放内存,只是销毁内存上的对象,但是指针仍然存在,仍然指向原来的内存,保存原来空间的地址。所以我们应该在释放之后将指针置空,以避免后面不小心解引用造成问题。
malloc 和 new的区别?
malloc/free是标准库函数,new/delete是C++运算符
malloc失败返回空,new失败抛异常
new/delete会调用构造、析构函数,malloc/free不会,所以他们无法满足动态对象的要求。
new返回有类型的指针,malloc返回无类型的指针 (很多编译器的new/delete都是以malloc/free为基础来实现的)
堆是操作系统维护的一块内存,而自由存储是C++中通过new与delete动态分配和释放对象的抽象概念。堆与自由存储区并不等价。自由存储区是两个动态内存区域之一,由new / delete分配/释放。对象生存期可以小于分配存储的时间;也就是说,自由存储对象可以在不立即初始化的情况下分配内存,并且可以在不立即释放内存的情况下将其销毁。在分配存储空间但在对象生命周期之外的时间段内,可以通过void *访问和操作存储空间,但不能访问原型对象的非静态成员或成员函数,也不能获取其地址,也不能对其进行其他操作。
堆是另一个动态内存区域,由malloc / free及其变体分配/释放。请注意,尽管默认的全局new和delete可能由malloc和特定编译器的free实现,但是堆与自由存储不同,并且在一个区域中分配的内存不能安全地在另一区域中释放。通过重新构造和显式销毁,可以将从堆分配的内存用于类类型的对象。如果这样使用,则有关自由存储对象生存期的注释在这里也类似适用。
特征 | new/delete | malloc/free |
---|---|---|
分配内存的位置 | 自由存储区 | 堆 |
返回类型安全性 | 完整类型指针 | void* |
内存分配失败返回值 | 默认抛出异常 | 返回NULL |
分配内存的大小 | 由编译器根据类型计算得出 | 必须显式指定字节数 |
处理数组 | 有处理数组的new版本new[] | 需要用户计算数组的大小后进行内存分配 |
已分配内存的扩充 | 无法直观地处理 | 使用realloc简单完成 |
是否相互调用 | 可以,看具体的operator new/delete实现 | 不可调用new |
分配内存时内存不足 | 无法通过用户代码进行处理 | 能够使用realloc函数或重新制定分配器 |
函数重载 | 允许 | 不允许 |
构造函数与析构函数 | 调用 | 不调用 |
c语言----与const常量有关的内存分配https://blog.csdn.net/silently_frog/article/details/96737764