赞
踩
int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
static int staticVar = 1;
int localVar = 1;
int num1[10] = { 1, 2, 3, 4 };
char char2[] = "abcd";
const char* pChar3 = "abcd";
int* ptr1 = (int*)malloc(sizeof(int) * 4);
int* ptr2 = (int*)calloc(4, sizeof(int));
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
free(ptr1);
free(ptr3);
}
选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
【说明】
为什么要分这些区域
这些区域哪个区域是我们需要重点关注的?
void Test()
{
int* p1 = (int*)malloc(sizeof(int));
free(p1);
// 1.malloc/calloc/realloc的区别是什么?
int* p2 = (int*)calloc(4, sizeof(int));
int* p3 = (int*)realloc(p2, sizeof(int) * 10);
// 这里需要free(p2)吗?
free(p3);
}
【面试题】
malloc/calloc/realloc的区别?
void Test()
{
// 动态申请一个int类型的空间
int* ptr4 = new int;
// 动态申请一个int类型的空间并初始化为10
int* ptr5 = new int(10);
// 动态申请10个int类型的空间
int* ptr6 = new int[3];
delete ptr4;
delete ptr5;
delete[] ptr6;
}
int* p2 = new int[10] {1, 2, 3, 4, 5, 6, 7};
注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[],注意:匹配起来使用。
class A { public: A(int a = 0) : _a(a) { cout << "A():" << this << endl; } ~A() { cout << "~A():" << this << endl; } private: int _a; }; int main() { // new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数 A* p1 = (A*)malloc(sizeof(A)); A* p2 = new A(1); free(p1); delete p2; // 内置类型是几乎是一样的 int* p3 = (int*)malloc(sizeof(int)); // C int* p4 = new int; free(p3); delete p4; A* p5 = (A*)malloc(sizeof(A) * 10); A* p6 = new A[10]; free(p5); delete[] p6; return 0; }
小结:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会。
struct ListNode { ListNode* _next; int _val; ListNode(int val) :_next(nullptr) , _val(val) {} }; // 创建的不带哨兵位 ListNode* CreateList(int n) { ListNode head(-1); // 哨兵位 ListNode* tail = &head; int val; printf("请依次输入%d个节点的值:>", n); for (int i = 0; i < n; i++) { cin >> val; tail->_next = new ListNode(val); tail = tail->_next; } return head._next; } void func() { int n = 1; while (1) { int* p = new int[1024 * 1024 * 100]; cout << n << "->" << p << endl; ++n; } } int main() { // 1、用法上,变简洁了 int* p0 = (int*)malloc(sizeof(int)); int* p1 = new int; int* p2 = new int[10]; // new 10个int对象 // 2、可以控制初始化 int* p3 = new int(10); // new 1个int对象,初始化成10 int* p4 = new int[10] { 1, 2, 3, 4, 5 }; // 3、自定义类型,开空间+构造函数 // 4、new失败了以后抛异常,不需要手动检查 ListNode* node1 = new ListNode(1); ListNode* node2 = new ListNode(2); ListNode* node3 = new ListNode(3); //... ListNode* list1 = CreateList(5); delete p3; delete[] p4; delete p1; delete[] p2; // 抛异常 try { func(); } catch (const exception& e) { cout << e.what() << endl; } return 0; }
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
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 // 如果申请内存失败了,这里会抛出bad_alloc 类型异常 static const std::bad_alloc nomem; _RAISE(nomem); } return (p); } /* operator delete: 该函数最终是通过free来释放空间的 */ void operator delete(void* pUserData) { _CrtMemBlockHeader* pHead; RTCCALLBACK(_RTC_Free_hook, (pUserData, 0)); if (pUserData == NULL) return; _mlock(_HEAP_LOCK); /* block other threads */ __TRY /* get a pointer to memory block header */ pHead = pHdr(pUserData); /* verify block type */ _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)); _free_dbg(pUserData, pHead->nBlockUse); __FINALLY _munlock(_HEAP_LOCK); /* release other threads */ __END_TRY_FINALLY return; } /* free的实现 */ #define free(p) _free_dbg(p, _NORMAL_BLOCK)
通过上述两个全局函数的实现知道,operator new 实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。
我们通过汇编也可以看到,这些最后还是分别调用malloc和free的
class Stack { public: Stack() { _a = (int*)malloc(sizeof(int) * 4); _top = 0; _capacity = 4; } ~Stack() { free(_a); _top = _capacity = 0; } private: int* _a; int _top; int _capacity; }; int main() { A* ptr1 = new A; // operator new + 1次构造 A* ptr2 = new A[10]; // operator new[] + 10次构造 delete ptr1; // 1次析构 + operator delete delete[] ptr2; // 10次析构 + operator delete[] Stack* pst = new Stack; delete pst; return 0; }
[]
,还需要调用析构函数,这样就知道要释放多少了如果不显示写这个析构函数,就也不会多开4个字节,编译器会自动生成一个,编译器觉得什么也不干,自动就会优化掉了,也就不会调用了
而内置类型就不会多开,内置类型就不用调用析构函数
5.2 自定义类型
new的原理
delete的原理
new T[N]的原理
delete[]的原理
使用格式:
使用场景:
class A { public: A(int a = 0) : _a(a) { cout << "A():" << this << endl; } ~A() { cout << "~A():" << this << endl; } private: int _a; }; // 定位new/replacement new int main() { // p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行 A* p1 = (A*)malloc(sizeof(A)); new(p1)A; // 注意:如果A类的构造函数有参数时,此处需要传参 p1->~A(); free(p1); A* p2 = (A*)operator new(sizeof(A)); new(p2)A(10); p2->~A(); operator delete(p2); return 0; }
malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地方是:
void MemoryLeaks()
{
// 1.内存申请了忘记释放
int* p1 = (int*)malloc(sizeof(int));
int* p2 = new int;
// 2.异常安全问题
int* p3 = new int[10];
Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.
delete[] p3;
}
C/C++程序中一般我们关心两种方面的内存泄漏:
int main()
{
int* p = new int[10];
// 将该函数放在main函数之后,每次程序退出的时候就会检测是否存在内存泄漏
_CrtDumpMemoryLeaks();
return 0;
}
// 程序退出后,在输出窗口中可以检测到泄漏了多少字节,但是没有具体的位置
Detected memory leaks!
Dumping objects ->
{79} normal block at 0x00EC5FB8, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
总结一下:
内存泄漏非常常见,解决方案分为两种:
1、事前预防型。如智能指针等。
2、事后查错型。如泄漏检测工具。
最后本文就到这里结束了,感谢大家的收看,请多多指点~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。