提问人:btmatt 提问时间:10/9/2023 最后编辑:btmatt 更新时间:10/9/2023 访问量:114
void operator delete [](void*) 错误,仅在 Release 中使用 gcc 进行编译时
void operator delete [](void*) error, when compiling with gcc only in Release
问:
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_
我尝试更改删除功能,但我所做的一切都不起作用或导致编译时错误。
答:
rowm1_--
已经具有未定义的行为。不允许在指针所指向的数组边界之外递增或递减指针(但递增指针的除外是单个对象)。
出于这个原因,条件
rowm1_ ++;
if(rowm1_ != NULL )
没有意义。永远不可能通过递增指针来获取空指针。
我想这会导致编译器的分析确定您必须调用一个不是 结果的指针,因为唯一可以工作的方式是在您递增数组之前已经指向数组。因此,递增的指针不能相对于 返回的指针偏移,但这是必需的(否则又是未定义的行为)。delete[] rowm1_
new[]
rowm1_
new[]
此外,空指针检查本身是没有意义的。调用 null 指针是完全有效的。delete[]
您曾经从此警告中发出致命错误,这就是它无法编译的原因。-Werror
即使您删除以使其编译,生成的程序也会具有未定义的行为,并且编译器不太可能做出与警告类似的推理以优化代码,这可能会产生非常意想不到的结果。-Werror
如果这个库到目前为止有效,编译器没有做任何有害的优化,那主要是运气。在某些体系结构上,即使幼稚地编译代码也可能导致指针溢出导致意外行为。
编译器越来越善于检测和推理这样的 UB,以警告用户,但与此同时,他们也越来越善于利用相同的分析来优化代码,假设这种 UB 不会发生。我不能肯定地说任何当前的编译器执行的优化会导致这里出现问题,但我也不认为编译器供应商保证这已经定义了行为。因此,它是否/何时会以潜在的微妙方式破裂或多或少取决于机会。
由于代码提到了 C 兼容性:对指针算术的相同限制也适用于 C 语言。如果不额外分配一个(未使用的)元素,透明 1 索引的整个概念就无法像这样工作。
评论
rowm1_ ++
rowm1_
new T*[M]
destroy
if(v_ == NULL)
if(v_ != NULL)
rowm1_++
rowm1_ -- ;