常见的程序崩溃的原因:
- 由assert()触发的;
- 操作内存时,越界了;
- 内存没有被初始化就拿去用了;
- 栈溢出了;
- 使用了野指针。
总之,非法操作内存会对引起程序崩溃。
C中动态内存管理方式:
使用malloc/calloc/realloc/free进行动态内存管理。
malloc、calloc、realloc的相同点与不同点:
相同点:
- 都是在堆上开辟空间的。
- 都是通过free来释放空间的。
- 返回值:成功都是void*,失败都是NULL。
不同点:(体现在每个函数的功能和参数列表不同)
- malloc只负责把空间开辟出来。malloc(字节数)
- calloc(单个元素所占字节数的大小,需要的元素个数)
- realloc对空间进行扩增和缩减,
realloc(地址,新开辟空间的大小)
// 如果新的大小大于原内存大小,则新分配不会被初始化;
// 如果新的大小小于原内存大小,则可能会导致部分数据丢失
面试题:什么是内存泄露?如何检测一个系统是否存在内存泄露?
常见的内存泄露:
void MemoryLeaks()
{
//1.内存申请了,忘了释放
int *pstr = (int*)malloc(sizeof(int));
assert(pstr);
//2.逻辑不清,以为自己释放了,实际造成了内存泄露
int *p1 = (int*)malloc(sizeof(int));
int *p2 = (int*)malloc(sizeof(int));
p1 = p2;//p1指向p2的空间,p1原来指向的空间就找不到了,此时p1和p2指向同一块空间
free(p1);//p1将p2指向的空间释放了
free(p2);//p2又来释放,导致同一块空间释放多次
//3.溢出,将堆破坏了
char *pstr = (char*)malloc(5);//只开辟了5个空间
strcpy(pstr, "hello word!!!");//strcpy不会进行类型检测
free(pstr);
//4.释放时传入的地址和申请时的地址不相同
int *p = (int*)malloc(sizeof(int));
p++;
free(p);
}
C++内存管理方式:
C语言内存管理方式在C++中可以继续使用,但尽量使用自己的内存管理方式:C++中通过new和delete运算符进行动态内存管理
void TestNewDelete()
{
int* p1 = new int;//申请单个空间(4个字节)
//delete p1;
int* p2 = new int(10);//申请单个空间(4个字节)并将其初始化为10
//delete p2;
int* p3 = new int[10];//申请了10个一段连续的空间(40个字节)
//delete[] p3;
delete p1;
delete p2;
delete[] p3;
}
int main()
{
TestNewDelete();
return 0;
}
C语言动态申请内存空间须:
- 申请后要对其进行判空。
- 要传入想要申请的字节数。
- 若要接受开辟的这段空间,必须对其进行类型转化。
C++不需要。
注意:
1. new和delete是运算符,而不是函数;但malloc / calloc / realloc / free都是函数。
2. new和delete、new和delete[]一定要匹配使用!!!否则会出现内存泄露甚至程序发生崩溃。
malloc/free和new/delete比较
- 测试malloc和free、new和delete、new和delete[]都没有匹配起来时:
void Test()
{
int *p1 = (int*)malloc(sizeof(int));
int *p2 = (int*)malloc(sizeof(int));
int *p3 = new int;
int *p4 = new int;
int *p5 = new int[10];
int *p6 = new int[10];
delete p1;
delete[] p2;
free(p3);
delete[] p4;
free(p5);
delete p6;
}
int main()
{
Test();
return 0;
}
上述代码能通过编译,也能正常运行,不存在内存泄露
构造Test类,再来测试以下代码:
class Test
{
public:
Test()
{
cout << "Test:" << this << endl;
}
/*~Test()
{
cout << "~Test:" << this << endl;
}*/
};
void main()
{
Test *p1 = (Test*)malloc(sizeof(Test));
Test *p2 = (Test*)malloc(sizeof(Test));
delete p1;//此处会打印出析构函数体的内容,即此处调用了析构函数
delete[] p2;//程序发生崩溃,如果将析构函数屏蔽掉,则不会发生崩溃
Test *p3 = new Test;//此处会打印出构造函数体的内容,即此处调用了构造函数
Test *p4 = new Test;//此处会打印出构造函数体的内容,即此处调用了构造函数
free(p3);
delete[] p4;//程序发生崩溃
Test *p5 = new Test[10];//此处调用了10次构造函数
Test *p6 = new Test[10];//此处调用了10次构造函数
free(p5);//程序发生崩溃
delete p6;
}