void operator delete [](void*) 错误,仅在 Release 中使用 gcc 进行编译时

void operator delete [](void*) error, when compiling with gcc only in Release

提问人:btmatt 提问时间:10/9/2023 最后编辑:btmatt 更新时间:10/9/2023 访问量:114

问:

gcc (Ubuntu 12.3.0-1ubuntu1~23.04) 12.3.0 编译器警告标志 = -Wall;-Werror CMAKE_CXX_STANDARD_REQUIRED = 开

我正在使用一个库(旧版本的 TNT),它通过以下方式实现了一些线性代数功能:

/*
*
* Template Numerical Toolkit (TNT): Linear Algebra Module
*
* Mathematical and Computational Sciences Division
* National Institute of Technology,
* Gaithersburg, MD USA
*
*
* This software was developed at the National Institute of Standards and
* Technology (NIST) by employees of the Federal Government in the course
* of their official duties. Pursuant to title 17 Section 105 of the
* United States Code, this software is not subject to copyright protection
* and is in the public domain.  The Template Numerical Toolkit (TNT) is
* an experimental system.  NIST assumes no responsibility whatsoever for
* its use by other parties, and makes no guarantees, expressed or implied,
* about its quality, reliability, or any other characteristic.
*
* BETA VERSION INCOMPLETE AND SUBJECT TO CHANGE
* see http://math.nist.gov/tnt for latest updates.
*
*/



// C compatible matrix: row-oriented, 0-based [i][j] and 1-based (i,j) indexing
//

#ifndef CMAT_H
#define CMAT_H

#include "subscript.h"
#include "vec.h"
#include <cstdlib>
#include <cassert>
#include <iostream>
#ifdef TNT_USE_REGIONS
#include "region2d.h"
#endif

namespace TNT
{

    template <class T>
    class Matrix 
    {
    
      public:
    
        typedef Subscript   size_type;
        typedef         T   value_type;
        typedef         T   element_type;
        typedef         T*  pointer;
        typedef         T*  iterator;
        typedef         T&  reference;
        typedef const   T*  const_iterator;
        typedef const   T&  const_reference;
    
        Subscript lbound() const { return 1;}
     
      protected:
        Subscript m_;
        Subscript n_;
        Subscript mn_;      // total size
        T* v_;                  
        T** row_;           
        T* vm1_ ;       // these point to the same data, but are 1-based 
        T** rowm1_;

        // internal helper function to create the array
        // of row pointers
    
        void initialize(Subscript M, Subscript N)
        {
            mn_ = M*N;
            m_ = M;
            n_ = N;
    
            v_ = new T[mn_]; 
            row_ = new T*[M];
            rowm1_ = new T*[M];
    
            assert(v_  != NULL);
            assert(row_  != NULL);
            assert(rowm1_ != NULL);
    
            T* p = v_;              
            vm1_ = v_ - 1;
            for (Subscript i=0; i<M; i++)
            {
                row_[i] = p;
                rowm1_[i] = p-1;
                p += N ;
                
            }
    
            rowm1_ -- ;     // compensate for 1-based offset
        }
       
        void copy(const T*  v)
        {
            Subscript N = m_ * n_;
            Subscript i;
    
    #ifdef TNT_UNROLL_LOOPS
            Subscript Nmod4 = N & 3;
            Subscript N4 = N - Nmod4;
    
            for (i=0; i<N4; i+=4)
            {
                v_[i] = v[i];
                v_[i+1] = v[i+1];
                v_[i+2] = v[i+2];
                v_[i+3] = v[i+3];
            }
    
            for (i=N4; i< N; i++)
                v_[i] = v[i];
    #else
    
            for (i=0; i< N; i++)
                v_[i] = v[i];
    #endif      
        }
    
        void set(const T& val)
        {
            Subscript N = m_ * n_;
            Subscript i;
    
    #ifdef TNT_UNROLL_LOOPS
            Subscript Nmod4 = N & 3;
            Subscript N4 = N - Nmod4;
    
            for (i=0; i<N4; i+=4)
            {
                v_[i] = val;
                v_[i+1] = val;
                v_[i+2] = val;
                v_[i+3] = val; 
            }
    
            for (i=N4; i< N; i++)
                v_[i] = val;
    #else
    
            for (i=0; i< N; i++)
                v_[i] = val;
            
    #endif      
        }
        
        void destroy()
        {
            /* do nothing, if no memory has been previously allocated */
            if(v_ == NULL) return ;
    
            /* if we are here, then matrix was previously allocated */
            if(v_ != NULL) delete [] (v_);     
            if(row_ != NULL) delete [] (row_);
    
            /* return rowm1_ back to original value */
            rowm1_ ++;
            if(rowm1_ != NULL ) delete [] (rowm1_);
        }
    
    
      public:
    
        operator T**(){ return  row_; }
        operator T**() const { return row_; }
    
    
        Subscript size() const { return mn_; }
    
        // constructors
    
        Matrix() : m_(0), n_(0), mn_(0), v_(0), row_(0), vm1_(0), rowm1_(0) {};
    
        Matrix(const Matrix<T> &A)
        {
            initialize(A.m_, A.n_);
            copy(A.v_);
        }
    
        Matrix(Subscript M, Subscript N, const T& value = T())
        {
            initialize(M,N);
            set(value);
        }
    
        Matrix(Subscript M, Subscript N, const T* v)
        {
            initialize(M,N);
            copy(v);
        }

        // destructor
        //
        ~Matrix()
        {
            destroy();
        }
    
    
        // reallocating
        //
        Matrix<T>& newsize(Subscript M, Subscript N)
        {
            if(num_rows() == M && num_cols() == N)
                return *this;
    
            destroy();
            initialize(M,N);
            
            return *this;
        }
    
    
        // assignments
        //
        Matrix<T>& operator=(const Matrix<T> &A)
        {
            if(v_ == A.v_)
                return *this;
    
            if(m_ == A.m_  && n_ == A.n_)      // no need to re-alloc
                copy(A.v_);
    
            else
            {
                destroy();
                initialize(A.m_, A.n_);
                copy(A.v_);
            }
    
            return *this;
        }
            
        Matrix<T>& operator=(const T& scalar)
        { 
            set(scalar); 
            return *this;
        }
    
    
        Subscript dim(Subscript d) const 
        {
    #ifdef TNT_BOUNDS_CHECK
           assert( d >= 1);
            assert( d <= 2);
    #endif
            return (d==1) ? m_ : ((d==2) ? n_ : 0); 
        }
    
        Subscript num_rows() const { return m_; }
        Subscript num_cols() const { return n_; }
    
    
    
    
        inline T* operator[](Subscript i)
        {
    #ifdef TNT_BOUNDS_CHECK
            assert(0<=i);
            assert(i < m_) ;
    #endif
            return row_[i];
        }
    
        inline const T* operator[](Subscript i) const
        {
    #ifdef TNT_BOUNDS_CHECK
            assert(0<=i);
            assert(i < m_) ;
    #endif
            return row_[i];
        }
    
        inline reference operator()(Subscript i)
        { 
    #ifdef TNT_BOUNDS_CHECK
            assert(1<=i);
            assert(i <= mn_) ;
    #endif
            return vm1_[i]; 
        }
    
        inline const_reference operator()(Subscript i) const
        { 
    #ifdef TNT_BOUNDS_CHECK
            assert(1<=i);
            assert(i <= mn_) ;
    #endif
            return vm1_[i]; 
        }
    
    
    
        inline reference operator()(Subscript i, Subscript j)
        { 
    #ifdef TNT_BOUNDS_CHECK
            assert(1<=i);
            assert(i <= m_) ;
            assert(1<=j);
            assert(j <= n_);
    #endif
            return  rowm1_[i][j]; 
        }
    
    
        
        inline const_reference operator() (Subscript i, Subscript j) const
        {
    #ifdef TNT_BOUNDS_CHECK
            assert(1<=i);
            assert(i <= m_) ;
            assert(1<=j);
            assert(j <= n_);
    #endif
            return rowm1_[i][j]; 
        }
    
    
    
    #ifdef TNT_USE_REGIONS
    
        typedef Region2D<Matrix<T> > Region;
        
    
        Region operator()(const Index1D &I, const Index1D &J)
        {
            return Region(*this, I,J);
        }
    
    
        typedef const_Region2D< Matrix<T> > const_Region;
        const_Region operator()(const Index1D &I, const Index1D &J) const
        {
            return const_Region(*this, I,J);
        }
    
    #endif
  
    };   //End public declarations
    
    
    /* ***************************  I/O  ********************************/
    
    template <class T>
    std::ostream& operator<<(std::ostream &s, const Matrix<T> &A)
    {
        Subscript M=A.num_rows();
        Subscript N=A.num_cols();
    
        s << M << " " << N << "\n";
    
        for (Subscript i=0; i<M; i++)
        {
            for (Subscript j=0; j<N; j++)
            {
                s << A[i][j] << " ";
            }
            s << "\n";
        }
    
    
        return s;
    }
    
    template <class T>
    std::istream& operator>>(std::istream &s, Matrix<T> &A)
    {
    
        Subscript M, N;
    
        s >> M >> N;
    
        if( !(M == A.num_rows() && N == A.num_cols() ))
        {
            A.newsize(M,N);
        }
    
    
        for (Subscript i=0; i<M; i++)
            for (Subscript j=0; j<N; j++)
            {
                s >>  A[i][j];
            }
    
    
        return s;
    }
    
    // *******************[ basic matrix algorithms ]***************************
    
    
    template <class T>
    Matrix<T> operator+(const Matrix<T> &A, 
        const Matrix<T> &B)
    {
        Subscript M = A.num_rows();
        Subscript N = A.num_cols();
    
        assert(M==B.num_rows());
        assert(N==B.num_cols());
    
        Matrix<T> tmp(M,N);
        Subscript i,j;
    
        for (i=0; i<M; i++)
            for (j=0; j<N; j++)
                tmp[i][j] = A[i][j] + B[i][j];
    
        return tmp;
    }
    
    template <class T>
    Matrix<T> operator-(const Matrix<T> &A, 
        const Matrix<T> &B)
    {
        Subscript M = A.num_rows();
        Subscript N = A.num_cols();
    
        assert(M==B.num_rows());
        assert(N==B.num_cols());
    
        Matrix<T> tmp(M,N);
        Subscript i,j;
    
        for (i=0; i<M; i++)
            for (j=0; j<N; j++)
                tmp[i][j] = A[i][j] - B[i][j];
    
        return tmp;
    }
    
    template <class T>
    Matrix<T> mult_element(const Matrix<T> &A, 
        const Matrix<T> &B)
    {
        Subscript M = A.num_rows();
        Subscript N = A.num_cols();
    
        assert(M==B.num_rows());
        assert(N==B.num_cols());
    
        Matrix<T> tmp(M,N);
        Subscript i,j;
    
        for (i=0; i<M; i++)
            for (j=0; j<N; j++)
                tmp[i][j] = A[i][j] * B[i][j];
    
        return tmp;
    }
    
    
    template <class T>
    Matrix<T> transpose(const Matrix<T> &A)
    {
        Subscript M = A.num_rows();
        Subscript N = A.num_cols();
    
        Matrix<T> S(N,M);
        Subscript i, j;
    
        for (i=0; i<M; i++)
            for (j=0; j<N; j++)
                S[j][i] = A[i][j];
    
        return S;
    }
    
    
        
    template <class T>
    inline Matrix<T> matmult(const Matrix<T>  &A, 
        const Matrix<T> &B)
    {
    
    #ifdef TNT_BOUNDS_CHECK
        assert(A.num_cols() == B.num_rows());
    #endif
    
        Subscript M = A.num_rows();
        Subscript N = A.num_cols();
        Subscript K = B.num_cols();
    
        Matrix<T> tmp(M,K);
        T sum;
    
        for (Subscript i=0; i<M; i++)
        for (Subscript k=0; k<K; k++)
        {
            sum = 0;
            for (Subscript j=0; j<N; j++)
                sum = sum +  A[i][j] * B[j][k];
    
            tmp[i][k] = sum; 
        }
    
        return tmp;
    }
    
    template <class T>
    inline Matrix<T> operator*(const Matrix<T>  &A, 
        const Matrix<T> &B)
    {
        return matmult(A,B);
    }
    
    template <class T>
    inline int matmult(Matrix<T>& C, const Matrix<T>  &A, 
        const Matrix<T> &B)
    {
    
        assert(A.num_cols() == B.num_rows());
    
        Subscript M = A.num_rows();
        Subscript N = A.num_cols();
        Subscript K = B.num_cols();
    
        C.newsize(M,K);
    
        T sum;
    
        const T* row_i;
        const T* col_k;
    
        for (Subscript i=0; i<M; i++)
        for (Subscript k=0; k<K; k++)
        {
            row_i  = &(A[i][0]);
            col_k  = &(B[0][k]);
            sum = 0;
            for (Subscript j=0; j<N; j++)
            {
                sum  += *row_i * *col_k;
                row_i++;
                col_k += K;
            }
            C[i][k] = sum; 
        }
    
        return 0;
    }
    
    
    template <class T>
    Vector<T> matmult(const Matrix<T>  &A, const Vector<T> &x)
    {
    
    #ifdef TNT_BOUNDS_CHECK
        assert(A.num_cols() == x.dim());
    #endif
    
        Subscript M = A.num_rows();
        Subscript N = A.num_cols();
    
        Vector<T> tmp(M);
        T sum;
    
        for (Subscript i=0; i<M; i++)
        {
            sum = 0;
            const T* rowi = A[i];
            for (Subscript j=0; j<N; j++)
                sum = sum +  rowi[j] * x[j];
    
            tmp[i] = sum; 
        }
    
        return tmp;
    }
    
    template <class T>
    inline Vector<T> operator*(const Matrix<T>  &A, const Vector<T> &x)
    {
        return matmult(A,x);
    }

} // namespace TNT

#endif
// CMAT_H

析构函数的实现方式如下:

        void destroy()
        {
            /* do nothing, if no memory has been previously allocated */
            if(v_ == NULL) return ;
    
            /* if we are here, then matrix was previously allocated */
            if(v_ != NULL) delete [] (v_);     
            if(row_ != NULL) delete [] (row_);
    
            /* return rowm1_ back to original value */
            rowm1_ ++;
            if(rowm1_ != NULL )
                  delete[] rowm1_;
        }

编译 Debug 时,没有错误或警告。但是,我在编译 Release 时遇到错误。

具体而言,这会导致编译时错误:

error: ‘void operator delete [](void*)’ called on pointer ‘*(TNT::Matrix<double>*)((char*)this + 48).TNT::Matrix<double>::rowm1_’ with nonzero offset 8 [-Werror=free-nonheap-object]
  192 |             delete[] rowm1_;
      |             ^~~~~~~~~~~~~~~

C++ 标准版本号似乎无关紧要(即 11/14/17)。

此外,我在 MSVC 2019 的调试或发布模式下均未收到任何警告或错误。

我不明白为什么我收到这个错误。
此外,我只为变量而不是变量获取它,它似乎以相同的方式初始化和删除。
rowm1_row_

我尝试更改删除功能,但我所做的一切都不起作用或导致编译时错误。

C++ 删除运算符

评论

3赞 Some programmer dude 10/9/2023
“将rowm1_恢复到原来的价值”和......为什么?您在何时何地进行修改以指向返回的内容以外的其他位置?rowm1_ ++rowm1_new T*[M]
2赞 Some programmer dude 10/9/2023
顺便说一句,在检查后的功能中,您不需要额外的检查。destroyif(v_ == NULL)if(v_ != NULL)
2赞 463035818_is_not_an_ai 10/9/2023
请发布一个最小的可重现示例。当考虑发布的代码时,这看起来很可疑,没有意义,也许它与您没有显示的代码有关?rowm1_++
3赞 Sam Varshavchik 10/9/2023
“补偿从 1 开始的偏移”——帮自己一个巨大的忙:学习并适应 C++ 自然的从 0 开始的数组偏移。这些绕过肉袋偏见的技巧将永远以眼泪告终。更不用说未定义的行为了,就像在这种情况下一样。
3赞 molbdnilo 10/9/2023
顺便说一句,是未定义的,因为它试图递减数组外部的指针。rowm1_ -- ;

答:

1赞 user17732522 10/9/2023 #1

rowm1_--已经具有未定义的行为。不允许在指针所指向的数组边界之外递增或递减指针(但递增指针的除外是单个对象)。

出于这个原因,条件

rowm1_ ++;
if(rowm1_ != NULL )

没有意义。永远不可能通过递增指针来获取空指针。

我想这会导致编译器的分析确定您必须调用一个不是 结果的指针,因为唯一可以工作的方式是在您递增数组之前已经指向数组。因此,递增的指针不能相对于 返回的指针偏移,但这是必需的(否则又是未定义的行为)。delete[] rowm1_new[]rowm1_new[]

此外,空指针检查本身是没有意义的。调用 null 指针是完全有效的。delete[]

您曾经从此警告中发出致命错误,这就是它无法编译的原因。-Werror

即使您删除以使其编译,生成的程序也会具有未定义的行为,并且编译器不太可能做出与警告类似的推理以优化代码,这可能会产生非常意想不到的结果。-Werror

如果这个库到目前为止有效,编译器没有做任何有害的优化,那主要是运气。在某些体系结构上,即使幼稚地编译代码也可能导致指针溢出导致意外行为。

编译器越来越善于检测和推理这样的 UB,以警告用户,但与此同时,他们也越来越善于利用相同的分析来优化代码,假设这种 UB 不会发生。我不能肯定地说任何当前的编译器执行的优化会导致这里出现问题,但我也不认为编译器供应商保证这已经定义了行为。因此,它是否/何时会以潜在的微妙方式破裂或多或少取决于机会。

由于代码提到了 C 兼容性:对指针算术的相同限制也适用于 C 语言。如果不额外分配一个(未使用的)元素,透明 1 索引的整个概念就无法像这样工作。