GCC12下shared_ptr数组触发use-after-free警告,该警告是否有效?
问题背景
将GCC编译器升级至12.x版本后,编译特定代码时出现-Wuse-after-free警告,且当模板参数为大小小于3的数组时警告消失,需确认该警告是否有效。
代码示例
#include <memory> struct Test { inline static int i = 0; Test() { ++i; } ~Test() { --i; } }; struct MHeap { template<typename T> void manage() { using Td = std::remove_extent_t<T>; std::shared_ptr<T> ptr(new T(), std::default_delete<Td[]>{}); } }; int main() { MHeap mem; mem.manage<Test[3]>(); }
编译命令与警告信息
编译命令:
g++ -O2 -Wall main.cpp
警告信息:
In member function 'void MHeap::manage() [with T = Test [3]]', inlined from 'int main()' at <source>:24:25: <source>:17:32: warning: pointer used after 'void operator delete [](void*, std::size_t)' [-Wuse-after-free] 17 | std::shared_ptr<T> ptr(new T(), std::default_delete<Td[]>{}); | ^~~~~~~ In file included from /opt/compiler-explorer/gcc-12.3.0/include/c++/12.3.0/memory:75, from <source>:2: In member function 'typename std::enable_if<std::is_convertible<_Up (*)[], _Tp (*)[]>::value>::type std::default_delete<_Tp []>::operator()(_Up*) const [with _Up = Test; _Tp = Test]', inlined from 'std::__shared_count<_Lp>::__shared_count(_Ptr, _Deleter, _Alloc) [with _Ptr = Test*; _Deleter = std::default_delete<Test []>; _Alloc = std::allocator<void>; <template-parameter-2-4> = void; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]' at /opt/compiler-explorer/gcc-12.3.0/include/c++/12.3.0/bits/shared_ptr_base.h:958:11, inlined from 'std::__shared_count<_Lp>::__shared_count(_Ptr, _Deleter) [with _Ptr = Test*; _Deleter = std::default_delete<Test []>; <template-parameter-2-3> = void; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]' at /opt/compiler-explorer/gcc-12.3.0/include/c++/12.3.0/bits/shared_ptr_base.h:939:57, inlined from 'std::__shared_ptr<_Tp, _Lp>::__shared_ptr(_Yp*, _Deleter) [with _Yp = Test; _Deleter = std::default_delete<Test []>; <template-parameter-2-3> = void; _Tp = Test [3]; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]' at /opt/compiler-explorer/gcc-12.3.0/include/c++/12.3.0/bits/shared_ptr_base.h:1478:17, inlined from 'std::shared_ptr<_Tp>::shared_ptr(_Yp*, _Deleter) [with _Yp = Test; _Deleter = std::default_delete<Test []>; <template-parameter-2-3> = void; _Tp = Test [3]]' at /opt/compiler-explorer/gcc-12.3.0/include/c++/12.3.0/bits/shared_ptr.h:232:48, inlined from 'void MHeap::manage() [with T = Test [3]]' at <source>:17:28, inlined from 'int main()' at <source>:24:25: /opt/compiler-explorer/gcc-12.3.0/include/c++/12.3.0/bits/unique_ptr.h:132:11: note: call to 'void operator delete [](void*, std::size_t)' here 132 | delete [] __ptr; | ^~~~~~~~~~~~~~~
问题解答
1. 警告是否有效?
该警告完全有效,代码存在明确的未定义行为:
- 当
T=Test[3]时,new T()创建的是一个Test[3]类型的对象(即单个数组对象),返回的指针类型是Test(*)[3](指向数组的指针)。 std::shared_ptr<T>会将这个指针隐式转换为Test*(数组类型退化为指针类型),传递给std::default_delete<Td[]>(即std::default_delete<Test[]>)。std::default_delete<Test[]>的operator()调用delete[],要求传入的指针必须是new Test[N]返回的首元素指针,但这里传入的是指向整个数组的指针——虽然两者地址数值相同,但类型不匹配,属于C++标准中的未定义行为。GCC 12.x的优化器能够检测到这种指针误用,因此触发了-Wuse-after-free警告。
2. 为什么数组大小小于3时警告消失?
对于Test[2]这类小型数组,GCC在-O2优化下可能会将数组分配优化为栈上分配,或者编译器内部处理逻辑中未触发指针类型不匹配的检测,但这不代表代码是正确的——本质上的未定义行为依然存在,只是编译器未检测出来而已。
正确写法示例
如果意图是管理一个Test类型的数组,应该让shared_ptr持有数组首元素指针,并搭配对应的数组删除器,修改manage函数如下:
template<typename T> void manage() { using Td = std::remove_extent_t<T>; constexpr size_t arr_size = std::extent_v<T>; // 直接创建数组,shared_ptr<Td[]>自动匹配delete[] std::shared_ptr<Td[]> ptr(new Td[arr_size]); }
或者如果确实要管理单个数组对象(即Test[3]类型的对象),则应该使用匹配的删除器std::default_delete<T>:
template<typename T> void manage() { std::shared_ptr<T> ptr(new T(), std::default_delete<T>{}); }
内容的提问来源于stack exchange,提问作者partha biswas
相关产品推荐
相关产品推荐

