c/c++部分知识总结
1、C语言中malloc、calloc、realloc的不同
(1) malloc分配指定字节数的存储区。此存储区中的初始值不确定
(2) calloc为指定长度的对象,分配能容纳其指定个数的存储空间。该空间中的每一位(bit)都初始化为0
(3) realloc 更改以前分配区的长度(增加或减少)。当增加长度时,可能需将以前分配区的内容移到另一个足够大的区域,而新增区域内的初始值则不确定
.分配函数时再分配 realloc()使我们可以增、减以前分配区的长度(最常见的用法是增加该区)。如果先分配一个可容纳长度为512的数组的空间,并在运行时填充它,但又发现空间不够,则可调用realloc扩充该存储空间。如果在该存储区后有足够的空间可供扩充,则可在原存储区位置上向高地址方向扩充,并返回传送给它的同样的指针值。如果在原存储区后没有足够的空间,则realloc分配另一个足够大的存储区,将现存的5 1 2个元素数组的内容复制到新分配的存储区。因为这种存储区可能会移动位置,所以不应当使用任何指针指在该区中。
注意,realloc的最后一个参数是存储区的newsize(新长度),不是新、旧长度之差。作为一个特例,若ptr是一个空指针,则realloc的功能与malloc相同,用于分配一个指定长度newsize的存储区。
2、常见的内存泄露,C/C++如何进行内存泄露检测?
常见的内存泄露问题包括 :
堆内存泄露和系统资源泄露
简单的潜在堆内存丢失和缓冲区覆盖
来自资源错误管理的潜在堆内存丢失
未初始化的指针
两个错误的内存释放
悬空指针
(1)malloc/free 和 new/delete 没有配套使用,导致了内存泄露。
内存泄露检测
(2)配套使用
malloc\realloc ------ free
new \new[] ---------- delete \delete[]
3、malloc/free和 new/delete的区别
区别:malloc和free是c语言的申请内存的方式,c语言中没有构造这一概念,malloc只是申请空间,并不会初始化,当申请空间失败后返回0,而且free仅仅释放工作;
new int(内置类型) 不会调用构造函数,只有new +自定义类型才会调用构造函数。
New []一个类的时候,当我们自己定义一个析构函数时会在1前面多开辟4个字节,用来决定析构多少次,当使用系统的析构函数的时候,不会多开辟4个字节。
New申请空间的底层代码实现也是调用malloc来完成。不过对malloc进行了封装,c语言中当malloc申请空间失败后会返回0,c++中申请失败会抛异常,因此在c++中new一块空间不用检测是否申请到空间,直接使用就可以了。
``
底层实现如下:
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{ // try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0) //当申请空间失败抛异常
if (_callnewh(size) == 0)
{ // report no memory
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
4、new/new[]/delete/delete[]的执行过程
New 类-> operator new -> maloloc -> 构造函数
Delete -> operator delete -> free ->析构函数->free
New[] +类 -> operator new[]->operator new -> malloc ->构造函数
Delete[] ->operator delete[]->operator delete->析构函数->free
5、熟悉–>operator new 和定位new表达式
定位new表达式的用途,当一块空间使用了malloc 申请好了之后,但是malloc中没有构造函数,但是c++中需要调用构造函数证明一个类的生成,这时候就可以使用定位new表达式
Char* tmp = (char*)malloc(10)
New(tmp)AA //AA是一个类
6、一个类如何只在堆上创建对象
```
在C++中,类的对象建立分为两种,一种是静态建立,如A a;另一种是动态建立,如A* ptr=new A;这两种方式是有区别的。
1、静态建立类对象:是由编译器为对象在栈空间中分配内存,是通过直接移动栈顶指针,挪出适当的空间,然后在这片内存空间上调用构造函数形成一个栈对象。使用这种方法,直接调用类的构造函数。
2、动态建立类对象,是使用new运算符将对象建立在堆空间中。这个过程分为两步,第一步是执行operator new()函数,在堆空间中搜索合适的内存并进行分配;第二步是调用构造函数构造对象,初始化这片内存空间。这种方法,间接调用类的构造函数。
7、一个类如何只在栈上创建对象
```
编译器在为类对象分配栈空间时,会先检查类的析构函数的访问性,其实不光是析构函数,只要是非静态的函数,编译器都会进行检查。如果类的析构函数是私有的,则编译器不会在栈空间上为类对象分配内存。因此,将析构函数设为私有,类对象就无法建立在栈上了。代码如下:
class A
{
public:
A(){}
void destory(){delete this;}
private:
~A(){}
};
只能在栈上分配类对象
只有使用new运算符,对象才会建立在堆上,因此,只要禁用new运算符就可以实现类对象只能建立在栈上。虽然你不能影响new operator的能力(因为那是C++语言内建的),但是你可以利用一个事实:new operator 总是先调用 operator new,而后者我们是可以自行声明重写的。因此,将operator new()设为私有即可禁止对象被new在堆上。代码如下:
class A
{
private:
void* operator new(size_t t){} // 注意函数的第一个参数和返回值都是固定的
void operator delete(void* ptr){} // 重载了new就需要重载delete
public:
A(){}
~A(){}
};
``
8、扩展:探索malloc的实现方式
```
关于VirtualAlloc函数:
其实在windows中在对管理器上提供了几个函数用来创建、分配、释放和销毁堆空间(我们申请的空间是在堆上的),从而实现了分配算法。
HeapCreat:创建一个堆
HeapAlloc:在堆里分配空间
HeapFree:释放分配的内存
HeapDestroy:销毁一个堆
这里的HeapCreat便是创建一个堆空间它申请内存空间便是通过VirtualAlloc函数来实现的。HeapAlloc函数是在堆空间里面给用户分配小的内存空间,如果内存不足它也能通过VirtualAlloc向系统申请更多的内存。
Malloc函数:
malloc函数其实是上面几个Heap操作函数的包装。
堆空间是程序申请出来的一大块空间,当我们用malloc函数申请空间的时候大小是不确定的,有可能是很小的一块空间也有可能是很大的一块空间,所以对堆空间也是需要一定的方法进行管理的,当你需要申请空间的时候按照你申请的大小以一定方法分配给你。
这就是分配算法,分配算法有很多种,对于不同的场合是有不同的分配算法的。在这里只简单的描述一下几种简单的算法。
Malloc底层实现:http://www.cnblogs.com/MrListening/p/5538665.html
1. 空闲链表
2. 位图
位图有它的优点:
比空闲链表更加稳定,因为可以对数组进行备份,而且就算某个块损坏,也不会影响整个位图其他的块空间。
速度比较快还容易管理。
同时也有它的缺点:
也容易造成块的浪费,因为毕竟它是整数倍的分配。
当堆比较大的时候,可能这个位图会很大,数组很庞大,可能效率也并不像想象那么快。
3. 对象池
还有一种方法是对象池,也是把堆空间分成了大小相等的一些块,它是认为某些场合每次分配的空间都相等,所以每次就直接返回一个块的大小,它的管理方法可以是链表也可以是位图。因为不用每次查找合适的大小的内存返回,所以效率很高。
其实在实际的应用中,堆的分配算法有很多,上面的三种只是其中简单的几种,而且在实际分配中它是根据应用场景可能对应不同的分配算法,也可能是多种算法的结合。只有这样才会达到高效的这么一个原则。