C/C++ 内存分区与内存分配

内存分区

站在汇编语言的角度,一个程序分为: 代码段--CS   数据段--DS      堆栈段--SS     扩展段--ES
站在高级语言的角度,根据APUE,一个程序分为如下段: text      data(initialized)   bss      heap     stack  ;
存储时存在3个区域,分别是:代码段、数据段以及BSS段;
当程序被加载到内存单元时( 即运行,此时可称之为: 进程),则需要另外两个区:堆和栈
在x86处理器上的Linux,代码段从0x08048000单元开始,栈则从0xC0000000(栈底,高地址)往下(往低地址)增长。栈由栈帧组成,系统会为每个当前调用的函数分配一个栈帧。栈帧中存储了函数的局部变量、实参和返回值;
 
局部变量(static声明的除外)
(注:函数形参属于局部变量)
向下增长、先进后出
动态内存分配的
向上增长、先进先出
BSS 段
未初始化的全局变量、未初始化的静态变量
在程序载入时由内核将此段中数据初始化为0或空指针。
数据段
初始化的全局变量、初始化的静态变量
、常量(注意包括字符串常量)
可读可写
代码段
可执行代码
此区域通常为只读
注:
1、全局变量不初始化,在运行时内核赋值为0或NULL; 局部变量不初始化 值不定(不同编译器有不同的值)
2、 局部指针char *s = "abc";
//s为局部变量,存储在 栈区 ,"abc"为字符串 常量 ,存储在已初始化 数据区,只读!!!
指针变量和其他常见的(int、char)变量一样,只不过它存的是地址而已,在内存中存放的位置和其他变量一样的
 
 
 
例题
【补充】
字节转换
            1 B/byte(字节) = 8 bit(比特)
2^10    1 KB(千字节) = 1024 B/byte(字节)
2^20    1 MB = 1024 KB
2^30    1 GB = 1024 MB
2^40    1 TB = 1024 GB
            1 PB = 1024 TB
 
 
 
 

内存管理

 
C语言内存分配函数(在堆区操作)
1、malloc函数:申请的是虚拟内存;
原型:void* malloc (size_t size);
Allocates a block of size bytes of memory, returning a pointer to the beginning of the block.并不初始化,内存里面的值为随机值;
例:p = (int*) malloc( sizeof(int) * 20 )
 
2、calloc函数
原型:void* calloc (size_t num, size_t size)
Allocates a block of memory for an array of num elements, each of them size bytes long, and initializes all its bits to zero.
例:pData = (int*) calloc (20,sizeof(int));// 分配20个的数组分配内存块,每个元素为sizeof(int)大小,并均初始化为0
 
3、realloc函数
原型:void* realloc (void* ptr, size_t size);
Changes the size of the memory block pointed to by ptr.重新调整ptr指向内存的大小,只可以扩大
 
4、free函数:释放的也是虚拟内存;
原型:void free (void* ptr);
释放prt所指的内存块
 
 
 
 
C++ 内存分配函数
1、new / delete
定义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 的小额区块。

空间配置函数 allocate( )
stl_alloc.h文件中 __default_alloc_template 类模板 拥有配置器的标准接口函数 allocate( )
此函数首先判断区块大小,大于128bytes调用第一级配置器,小于128bytes (即用第二级配置器)就检查对应free list。
如果free list之内有可用的区块,就直接拿来用;
如果没有可用区块,就将区块大小上调至8倍数边界,然后调用refil1( ),准备为free list 重新填充空间 。而refill( )经由chunk_alloc函数从内存池中取得新区块(缺省20个),万一内存池空间不足以提供20个区块呢? chunk_alloc()函数以end_free - start_free来判断内存池的余量。
如果 余量 充足,就直接调出20个区块返回给free list。
如果 余量 不足以提供20个区块,但还足够供应一个以上的区块。这时候其pass by reference的nobjs参数将 被修改为实际能够供应的区块数
如果内存池连一个区块空间都无法供应,此时便需利用 malloc( )从heap中配置内存
万一山穷水尽,整个system heap空间都不够了,malloc( )行动失败,chunk_alloc()就四处寻找有无“尚有未用区块,且区块够大”之free lists。 找到了就挖一块交出,找不到就调用第一级配置器。第一级配置器其实也是使用malloc()来配置内存,但它有out-of-memory处理机制(类似new-handler 机制),或许有机会释放其它的内存拿来此处使用。如果可以,就成功,否则发出bad_aloc异常
 
 
空间释放函数 deallocate( )
stl_alloc.h文件中的__default_alloc_template类模板用有配置器的标准接口函数deallocate( ); 该函数首先判断区块大小,大于128bytes就调用第一级配置器,小于128bytes就找出对应的free list,将区块回收。

 

2、new[] / delete[]

定义1:int *p3 = new int[10] // 创建10个元素的数组
    delete[] p3;
    p3 = nullptr;
注:
1、对空指针使用delete不存在问题;
2、使用new [ ] 分配的内存应使用delete [ ] 来释放;
3、不能对同一内存使用两次delete;
 
 
 
【补充】
野指针的成因
1、指针变量未初始化;
2、指针操作超出变量的作用域;
3、指针释放之后未置空;
delete一个指针后,指针变成了 空悬指针 ,即指向一块曾经保存数据对象但现在已经无效的内存指针。
free/delete只是把指针所指的内存给释放掉了,但并没有把指针本身干掉,它仍保存那个无效的地址,因此释放完指针应立即将指针设置为 NULL/nullptr。但这只是提供了 有限的保护:看下例
    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;
}

【补充】

malloc/free 和 new/delete 的区别
1、new/delete是操作符;而malloc/free函数,需要相关头文件支持;
2、new/delete出分配/释放内存外,还会调用构造函数/析构函数;而malloc/free仅仅只是分配/释放内存;
 
 
 
 
  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值