You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GCC12下shared_ptr数组触发use-after-free警告,该警告是否有效?

GCC 12.x编译警告有效性分析:数组shared_ptr搭配default_delete的问题

问题背景

将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 20:16:00