void * realloc(void * p,size_t new_size)
如果p为NULL,则当malloc使用。
如果newsize为0,则释放p所指向的空间。返回NULL
如果内存不足,则原先p指向的空间不变,返回NULL
如果系统第一次分配的空间其后的空间大小可以满足newsize,则扩大原先指针P所指的空间,指针p的地址不变并返回。如果其后空间不够,则重新向系统申请空间,并把原先内存的数据拷贝到新的空间中,然后把新空间的地址赋给p,返回的同时释放原空间。
void* malloc(size_t size)
当内存充足时返回该大小的指针,内存不充足时返回NULL。
当申请大小为0的空间时,要么返回NULL。要么返回一个指针,但是不能对这个指针指向的内存进行写操作,如果写了free时崩溃掉。
一般而言对于低于128k大小的空间malloc使用sbrk系统调用申请,对于大于128k的空间使用mmap系统调用申请硬盘上的匿名虚存供进程使用。即使申请成功,系统也不是立即为进程分配相应的物理内存,当第一次访问时会发生缺页中断,系统这时才会为进程分配物理内存。
void free(void*p)
当释放NULL指针时,什么都不会发生,但是当释放一个非calloc,realloc,malloc申请的内存时,会发生未定义的行为,一般崩溃。当释放大于128字节的空间时。并且free时,释放内存并不会直接归还给内核,当大于128k时,free调用munmmap释放空间,内存直接归还,而小于128k时,会调用sbrk,堆顶指针会回溯。
如果malloc申请了100字节大小的空间,free时将返回的指针偏移一定数量返回时,会发生未定义的行为,一般崩溃,因为malloc申请空间时一般在32位下都会多申请8字节作为内存信息,如果释放不按原先长度释放free时会崩溃。
new/new[] delete/delete[]
new/new[] 应与 delete/delete[] 匹配使用,否则发生未定义行为。
具体是这样的大多数编译器,如果是内置类型则没什么,如果是自定义类型并且该类有显示的析构函数,那么这个时候new的时候实际会多分配4个字节,作为计数器来记录要析构的次数。所以 delete[] 会先调用所有对象的析构函数,最后再释放的时候把地址上移四字节,恢复到new[]申请的空间地址处,然后再释放。
如果我们用delete释放,则释放的地址与new申请的地址不是同一个地址,一般会发生崩溃行为。这个就像你malloc 了一段内存,free的时候的地址不是malloc申请的地址,那么肯定是未定义行为,这个未定义行为在大多数编译器下的表现是崩溃。
C++中STL中的空间配置器
int * volatile* p=0x0101;
这个指针代表,其二级指针所指的一级指针的这个变量是易变的
SGI_STL 二级空间配置器模型
在二级空间配置这个模型中,那个链表先调用refill函数进行内存填充,那个先连入内存池的头部。
整个模型初始化是这样的:
开始 start end 指向NULL
链表内所以指针都指向NULL
然后某个链表被allocate调用申请内存,链表也没内存就去向内存池要内存,内存池也没有。
内存池就调用了一级空配的allocate,实际就是调用了malloc,这时返回一个大块内存,start end看到了大块内存来了就立即抱上了大块内存的大腿。这时有资源的内存池就很阔绰的给了符合链表大小的20块小块内存,这时链表就内存了,把第一块内存返回user,剩下的连入链表。
所以这就是整个二级空间配置器初始化。
所以与其说把内存返回用户,把申请到的内存连入链表本质上都是在内存池中那一大块内存上操作的….实际都是指针造成的假象…。
所以整个二级空间配置器的模型的大块内存,最终是在进程退出由操作系统来回收的,所以不用担心内存泄漏。
VS 下的空间配置器
template<class _Ty> inline
_Ty *_Allocate(size_t _Count, _Ty *)
{ // allocate storage for _Count elements of type _Ty
void *_Ptr = 0;
if (_Count == 0)
;
else if (((size_t)(-1) / sizeof (_Ty) < _Count)
|| (_Ptr = ::operator new(_Count * sizeof (_Ty))) == 0)
_Xbad_alloc(); // report no memory
return ((_Ty *)_Ptr);
}
// TEMPLATE CLASS allocator
template<class _Ty>
class allocator
: public _Allocator_base<_Ty>
{ // generic allocator for objects of class _Ty
public:
typedef allocator<_Ty> other;
typedef _Allocator_base<_Ty> _Mybase;
typedef typename _Mybase::value_type value_type;
typedef value_type *pointer;
typedef const value_type *const_pointer;
typedef void *void_pointer;
typedef const void *const_void_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef false_type propagate_on_container_copy_assignment;
typedef false_type propagate_on_container_move_assignment;
typedef false_type propagate_on_container_swap;
allocator<_Ty> select_on_container_copy_construction() const
{ // return this allocator
return (*this);
}
template<class _Other>
struct rebind
{ // convert this type to allocator<_Other> //这个函数就是把自定义类型的空间配置器转换成默认的...
typedef allocator<_Other> other;
};
pointer address(reference _Val) const _NOEXCEPT
{ // return address of mutable _Val
return (_STD addressof(_Val));
}
const_pointer address(const_reference _Val) const _NOEXCEPT
{ // return address of nonmutable _Val
return (_STD addressof(_Val));
}
allocator() _THROW0()
{ // construct default allocator (do nothing)
}
allocator(const allocator<_Ty>&) _THROW0()
{ // construct by copying (do nothing)
}
template<class _Other>
allocator(const allocator<_Other>&) _THROW0()
{ // construct from a related allocator (do nothing)
}
template<class _Other>
allocator<_Ty>& operator=(const allocator<_Other>&)
{ // assign from a related allocator (do nothing)
return (*this);
}
void deallocate(pointer _Ptr, size_type)
{ // deallocate object at _Ptr, ignore size
::operator delete(_Ptr); // 只是简单的调用了 operator delete
}
pointer allocate(size_type _Count)
{ // allocate array of _Count elements
return (_Allocate(_Count, (pointer)0));
}
pointer allocate(size_type _Count, const void *)
{ // allocate array of _Count elements, ignore hint
return (allocate(_Count));
}
void construct(_Ty *_Ptr)
{ // default construct object at _Ptr
::new ((void *)_Ptr) _Ty();
}
void construct(_Ty *_Ptr, const _Ty& _Val)
{ // construct object at _Ptr with value _Val
::new ((void *)_Ptr) _Ty(_Val);
}
template<class _Objty,
class... _Types>
void construct(_Objty *_Ptr, _Types&&... _Args)
{ // construct _Objty(_Types...) at _Ptr
::new ((void *)_Ptr) _Objty(_STD forward<_Types>(_Args)...);
}
template<class _Uty>
void destroy(_Uty *_Ptr)
{ // destroy object at _Ptr
_Ptr->~_Uty();
}
size_t max_size() const _THROW0()
{ // estimate maximum array size
return ((size_t)(-1) / sizeof (_Ty));
}
};
void _Tidy()
{ // free all storage
if (this->_Myfirst != pointer())
{ // something to free, destroy and deallocate it
this->_Orphan_all();
_Destroy(this->_Myfirst, this->_Mylast);
this->_Getal().deallocate(this->_Myfirst,
this->_Myend - this->_Myfirst); //这个Getal 就是萃取了空间配置器的类型,然后转调该配置器的
this->_Myfirst = pointer();//deallocate函数... 默认vs下就是std::operator delete(ptr)
this->_Mylast = pointer();// 这个deallocate里面无视大小的... 直接调的这个delete把ptr指向的
this->_Myend = pointer();//空间直接释放掉 其实这个默认的空间配置器 很像SGI下的一级空配...
}
}
由此可见 vs 下空间配置器只是简单的封装了下operator new 和 operator delete ….
一般容器都调用Tidy函数来清理…..,Tidy里面 做了类型萃取,然后萃取了空间配置器的类型
再统一通过一个接口 wrap_alloc 的接口来调用 deallocate 最后来归还空间…
所以 vs 下的空间配置器很像SGI 下的一级空间配置器