c语言sscanf函数的用法是什么
268
2022-09-13
理解malloc、free、new、delete
malloc和new的区别
malloc是按字节开辟空间的,new开辟内存时需要指定类型(new int()),malloc开辟内存返回的都是void*,而new返回的是对应类型的指针malloc负责开辟空间,new不仅有malloc的功能,还可以进行数据初始化和构造对象,比如:new int(10)。new有开辟空间和构造的功能。malloc开辟内存失败返回nullptr,而new则会抛出bad_alloc异常我们调用new实际上是调用的operator new,可以重载operator new
free和delete的区别
delete先调用析构函数,再释放空间(即free)我们调用delete实际上是调用的operator delete,可以重载operator delete
我们先重载一下new、delete,添加测试类
// new实际上先调用operator new开辟内存空间,然后调用对象的构造函数void* operator new(size_t size) { void* p = malloc(size); if (p == nullptr) { throw bad_alloc(); } cout << "operator new addr:"<< p << endl; return p;}// delete实际上先调用对象的析构函数,然后调用operator delete回收内存空间,void operator delete(void* ptr) { cout << "operator free addr:" << ptr << endl; free(ptr);}// 用于数组void* operator new[](size_t size) { void* p = malloc(size); if (p == nullptr) { throw bad_alloc(); } cout << "operator new[] addr:" << p << endl; return p;}void operator delete[](void* ptr) { cout << "operator free[] addr:" << ptr << endl; free(ptr);}class Test {public: Test(int data = 10) :ma(data) { cout << "Test()" << endl; } ~Test() { cout << "~Test()" << endl; }private: int ma;};
存在自定义构造析构函数,new[]
int main() { try { Test* p = new Test[5]; cout << "开辟空间后返回的地址:" << p << endl; delete[] p; // 调用析构,不会查看前4个字节确定数组元素的个数 // delete[]p; //会查看前4个字节确定数组元素的个数 } catch (const bad_alloc& err) { cerr << err.what() << endl; } return 0;}
可以看到,我们要分配5个Test对象的空间,这5个对象的空间为20字节,而size却是24,还多出了4字节空间。是的,malloc就是需要多分配4字节空间用于存储对象的个数,以便delete[]执行指定次数析构函数
delete正确执行析构函数了,还要释放空间,即free,那free怎么知道要释放多少空间?
malloc会多分配4字节空间用于存放5个Test对象的个数,然后返回开辟空间的地址,在这个地址之前还有8字节,叫做块头,存放了真正给用户动态开辟的空间大小,也就是24
delete[]在释放空间的时候,会在用户传入地址往前查看4字节,执行指定次数析构函数后再执行free,free会在这24字节空间再往前偏移8字节,查看需要释放的内存大小(24),连同块头的8字节一起释放
当前场景下是内存这样,而为了防止病毒任意修改内存信息,某些平台可能会做一定的优化,比如在块头和分配的内存之间存在的填充
无自定义析构函数,new[]
测试类如下:
class Test {public: Test(int data = 10) :ma(data) { cout << "Test()" << endl; } private: int ma;};
测试代码如下:
int main() { try { Test* p = new Test[5]; cout << "开辟空间后返回的地址:" << p << endl; delete[] p; // 调用析构,不会查看前4个字节确定数组元素的个数 // delete[]p; //会查看前4个字节确定数组元素的个数 } catch (const bad_alloc& err) { cerr << err.what() << endl; } return 0;}
此时传入new[]的size变成了20,少了我们之前用于存储对象个数的4字节空间
对于内置的数据类型和没有自定义析构函数的类类型,new和new[]分配空间的时候,不会额外分配4字节存储对象的个数,依然会存在8字节的块头。只要是不额外分配4字节存储对象的个数,new和new[]、delete和delete[]混用不会出错
开辟方式 | 释放方式 | 结果 |
new | delete | 成功 |
new[] | delete | 内嵌类型和不提供自定义析构函数成功;提供自定义析构函数失败 |
new | delete[] | 内嵌类型和不提供自定义析构函数成功;提供自定义析构函数失败 |
new[] | delete[] | 成功 |
对于提供自定义析构函数的类类型,用new[]开辟空间,用delete释放空间出错。new[]会开辟4字节存储对象的个数,执行delete时,直接往前偏移8字节获取开辟的内存大小,由于存在4字节内存存储对象的个数,实际需要偏移12字节才能获取块头信息进行空间正确释放。此外,只执行可一个对象的析构函数,而new[]构造了一个对象数组,这也不正确。
同理,对于提供自定义析构函数的类类型,用new开辟空间,用delete[]释放空间也出错。因为new没有开辟4字节存储对象个数,而delete以为存在这4字节,执行析构函数的次数就从块头获取了,这不正确。想要访问块头,执行delete[]时会偏移12字节,越过了块头,这也会出问题。
参考:为什么new/delete和new[]/delete[]必须配对使用?
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~