使用std::make_unique创建数组触发malloc(): corrupted top size错误排查
std::unique_ptr 创建方式引发内存错误的原因解析
在C++开发中,不同方式创建std::unique_ptr会导致两种结果:程序正常运行,或触发malloc(): corrupted top size堆损坏错误。下面分别解析两种错误写法的问题根源。
第一种错误写法(触发内存错误)
#include <tuple> #include <string> #include <memory> #include <fstream> #include <iostream> #include <filesystem> std::tuple<std::unique_ptr<uint8_t*>, size_t> getBinaryData( const std::filesystem::path &sourcePath ) { std::ifstream filestream ( sourcePath, std::ios::binary ); const size_t filesize = std::filesystem::file_size(sourcePath); std::cout << "Input file size: " << filesize << " bytes\n"; std::unique_ptr<uint8_t*> buffer = std::make_unique<uint8_t*>( new uint8_t[ filesize ] ); if ( !filestream.read( ( char* )( buffer.get() ), filesize ) ) { std::cout << "Reading from file " << sourcePath << " failed." << "\n"; } filestream.close(); return { std::move( buffer ), filesize }; } int main() { auto [memoryPtr, length] = getBinaryData( "myfile.bin" ); }
错误原因
- 读写地址完全错误:
std::unique_ptr<uint8_t*>是指向uint8_t*类型的智能指针,buffer.get()返回的是uint8_t**(指针的指针)。将其强转为char*传给filestream.read()时,程序并未把文件数据写入new uint8_t[filesize]分配的数组内存,而是直接写入智能指针内部存储的指针变量的内存地址。若文件大小超过指针变量的字节数(通常为8字节),就会越界覆盖堆上的其他数据,直接破坏堆结构,触发malloc(): corrupted top size错误。 - 内存释放方式不匹配:
unique_ptr销毁时会默认用delete释放它管理的uint8_t*指针,而非delete[]释放数组,这会导致数组内存泄漏,但更致命的是前面的越界写已经破坏了堆结构。
第三种错误写法(同样失效)
#include <tuple> #include <string> #include <memory> #include <fstream> #include <iostream> #include <filesystem> std::tuple<std::unique_ptr<uint8_t*>, size_t> getBinaryData( const std::filesystem::path &sourcePath ) { std::ifstream filestream ( sourcePath, std::ios::binary ); const size_t filesize = std::filesystem::file_size(sourcePath); std::cout << "Input file size: " << filesize << " bytes\n"; std::unique_ptr<uint8_t*> buffer( new uint8_t[ filesize ] ); if ( !filestream.read( ( char* )( buffer.get() ), filesize ) ) { std::cout << "Reading from file " << sourcePath << " failed." << "\n"; } filestream.close(); return { std::move( buffer ), filesize }; }
失效原因
- 类型不匹配导致读写错误:
new uint8_t[filesize]返回uint8_t*类型,但std::unique_ptr<uint8_t*>的构造函数需要uint8_t**类型。即便某些编译器允许隐式转换,本质也是把数组首地址当成指向指针的指针存储。和第一种写法一样,buffer.get()返回uint8_t**,强转成char*后依然是把文件数据写入指针变量的内存地址,越界破坏堆结构。 - 释放方式不匹配:智能指针销毁时仍用
delete释放单个指针,而非delete[]释放数组,进一步加剧堆损坏问题。
补充:正确写法的逻辑
第二种写法能正常运行,是因为std::unique_ptr<uint8_t, std::default_delete<uint8_t[]>>明确指定了数组版本的删除器,确保用delete[]释放数组;同时buffer.get()返回uint8_t*,强转成char*后指向数组首地址,文件数据会正确写入数组内存,不会越界。
内容的提问来源于stack exchange,提问作者binaryBigInt
相关产品推荐
相关产品推荐

