内存分区
栈
|
局部变量(static声明的除外)
(注:函数形参属于局部变量)
|
向下增长、先进后出
|
堆
|
动态内存分配的
|
向上增长、先进先出
|
BSS 段
|
未初始化的全局变量、未初始化的静态变量
|
在程序载入时由内核将此段中数据初始化为0或空指针。
|
数据段
|
初始化的全局变量、初始化的静态变量
、常量(注意包括字符串常量)
|
可读可写
|
代码段
|
可执行代码
|
此区域通常为只读
|
内存管理
定义1:int *p1 = new int(1024); // 初始值为1024
delete p1;
p1 = NULL;
vector<int> *pv = new vector<int> {0,1,2,3,4,5,6,7,8,9}; // 10个元素,0 - 9
string *ps = new string();// 默认初始化为空串
int *pp = new int(); // 默认初始化为0
定义2:int *p2 = new int; // p2指向一个动态分配的、未初始化的无名对象
delete p2;
使用new分配cosnst 对象是合法的;const 对象必须进行初始化,
const int *p = new const int (5);
而在上述利用new在堆上创建对象时,实际上是做了3件事:分配内存、调用对象的构造函数、返回正确的指针;当然,创建的是简单的类型变量第二步就被省略了。下面的部分是摘录《STL源码分析》中空间配置器部分的讲解来new的底层实现。
class Foo{....};
Foo* p = new Foo; // 配置内存,构造对象;
...
delete p; // 对象析构,释放内存;
关键字new在堆上动态创建一个对象时,,new内含三阶段操作(当然对于简单类型第二步是没有的)
(1)先调用::operator new 配置内存,由alloc:allocate( )负责;
(2)再调用Foo::Foo()构造对象内容,由::construct( )负责;
(3)返回指针;
delete算式也内含两阶段操作:
(1)先调用Foo::~Foo()将对象析构,由alloc:deallocate( )负责;
(2)再调用::operator delete释放内存,由::destory( )负责;
对象构造前的空间配置和对象析构后的空间释放,由<st1-alloc.h>负责,C++的内存配置基本操作是::operator new( ),内存释放基本操作是::operator delete( ). 这两个全局函数相当于C的ma1loc( )和free( )函数。是的,正是如此,SGI正是以malloc( )和free( )完成内存的配置与释放。
考虑到小型区块(小于128bytes时)所可能造成的内存碎片问题,SGI设计了双层级配置器,第一级配置器直接使用mal1oc( )和free( );当配置区超过128bytes视为足够大,便调用第一级配置器。第二级配置器则视情况采用不同的策略。
若定义 宏__USE_MALLOC,则直接调用第一级配置器;否则SGI默认的第二级配置器__default_alloc_template
一、二级配置器的关系,接口包装,及实际运用方式如下:
一级配置器以malloc、free()、realloc( )等C函数执行实际的内存配置、释放、重配操作;因为它并非使C++的::operator new 操作,因此他不能直接运行C++ 的new-handler机制。
【补充】C++new handler机制
C++new handler机制是,你可以要求系统在内存配置需求无法被满足时,调用一个你所指定的函数。换句话说,一旦::operator new未获满足的内存需求,在抛出std:bad_alloc异常状态之前,会先调用由客端指定的处理例程。该处理例程通常即被称为new-handler。( 《Effective C++》P270)
二级配置器,当区块大于128bytes则交由第一级配置器处理,小于128bytes时,则以内存池(memory pool)管理或称次层配置:每次配置一大块内存,并维护对应之自由链表(free-list)。下次若再有相同大小的内存需求,就直接从free-lists中拨出。如果客端释还小额区块,就由配置器回收到free-lists。SGI第二级配置器主动将任何小额区块的内存需求量上调至8的倍数(例如客端要求30bytes,就自动调整为32bytes),并维护16个free-lists,各自管理大小分别为8,16,24,32,40,48,56,64,72,80,88,96,104,112,120,128 bytes 的小额区块。
2、new[] / delete[]
int *p = new int(502);
int *q = p;
delete p;
p = nullptr;
p 和 q 指向相同的动态分配的对象,delete p所指的内存后置为nullptr,但是 q 仍然指向刚刚释放掉的内存,q 变为无效在实际系统中,查找指向相同内存的所有指针是十分困难的。可以使用智能指针解决。
int main()
{
int *p;// p没有指向确定指针,随机指向一个地址,十分的危险
printf("0x%x \n", p);
*p = 5;
printf("%d \n", *p); // 若无上句的赋值,将非法访问内存,程序奔溃
return 0;
}
【补充】