2

我有一个 c++ 代码,我使用 MSC9 来编译它。它一直在随机崩溃。例如,如果使用 `` 从 Perl 调用它会崩溃,但从命令行或 Ultimate++ 调用它不会崩溃。

我的意思是从 perl 中调用它,例如。f.exe arg1 arg2 arg3

堆栈跟踪没有显示太多。逐行跟踪程序证明程序在返回时最后失败...

所以就是这样

int funcname()
{
    return 0; <-- crashing after that...
}

我猜堆栈已损坏,堆栈展开后,它崩溃了..

什么会导致它?该程序使用 pcre、stl 和迭代器。迭代器可以破坏堆栈吗?你会如何捕捉到这样的错误?

它可能是编译器错误吗?

注意:调试版不会崩溃,只有发布版...

该错误似乎与此 pvector 类有关。

我有一个类似于这样的结构:

struct complexstr
{
 pvector<int> v;
 string v2;
 hash_map<string> hm;
 vector<string> vs; // similar
 int i;
};

它似乎失败了,因为这一行:

complexstr s1;
complexstr s2;

s2=s1; // it seems to fail here, if this is not there... there is no error.

我认为问题出在下面的类... std::copy 在 pvector operator=(const pvector &pv) 中是正确的,对吗?

pvector 是一个 perl 兼容的向量...它的索引可以大于向量的分配大小。

更新 1:我收到了作业中存在泄漏的建议。我改变了分配......现在看起来是这样的:

 pvector& operator=(const pvector &pv)
  {
    delete [] m_rgArray;  
    m_rgArray=new value_type[pv.allocated];
    m_nIndex=pv.m_nIndex;
    allocated=pv.allocated;
    std::copy(pv.m_rgArray, pv.m_rgArray + pv.allocated, m_rgArray);  
    return *this;
  }

注意:通过将 & 添加到返回类型,崩溃仍然存在。但是,删除泄漏后,添加 delete [] m_rgArray; ,程序不再崩溃。我不明白。据我所知,泄漏不会导致崩溃。所以问题似乎解决了(?)。问号表示我的惊讶。Update2:不,问题又回来了。它只是消失了一段时间。Update3:我想我已经找到了。我使用了来自 Microsoft 调试工具的实用程序 gflags.exe 和 windbg.exe 来查找确切位置。我使用 gflags.exe /p /enable myprog.exe /full 来打开堆错误的异常。目前,我认为该错误是由 FindClose(handle); 引起的。其中句柄是一个随机值,未初始化。

旧版:

 template<class _Ty>
  class pvector
  {
    public:
    _Ty * m_rgArray; // Declare array
    int m_nIndex; // Index to array
    int allocated;
    _Ty undefvalue;
    typedef _Ty value_type;
    typedef value_type & reference;
    typedef const value_type & const_reference;
    typedef custom_iterator<_Ty> iterator;
    typedef custom_iterator<_Ty> const_iterator;
    typedef int difference_type;
    typedef int size_type;
    //typedef typename pvector_type_traits<_Ty>::default_value default_value;

    pvector() : m_nIndex(0) 
    { // init index to 0
      m_rgArray = new value_type[10];
      allocated = 10;
      fill(0);
    }

    pvector(size_type s) : m_nIndex(0) 
    { // init index to 0
      size_type defsize = 10;
      if (s>10)
      {
        defsize = s;
      }
      m_rgArray = new value_type[defsize];
      allocated = defsize;
      fill(0);
    }
      pvector(pvector const& pv)
    : m_rgArray(new value_type[pv.allocated]),
    m_nIndex(pv.m_nIndex),allocated(pv.allocated)
    {
     std::copy(pv.m_rgArray, pv.m_rgArray + pv.allocated, m_rgArray);     
    }

    pvector operator=(const pvector &pv)
    {
    m_rgArray=new value_type[pv.allocated];
    m_nIndex=pv.m_nIndex;
    allocated=pv.allocated;
    std::copy(pv.m_rgArray, pv.m_rgArray + pv.allocated, m_rgArray);  
    return *this;
    }
    void clear()
    {
       m_nIndex=0; 
       fill(allocated);    
    }

    ~pvector() {
     delete []m_rgArray; 
    }

    size_type size() const
    { // return length of sequence
      return m_nIndex;
    }

    size_type max_size() const
    { // return maximum possible length of sequence
      return 0;
    }

    void fill(size_type si)
    {
      for (size_type i = si;i<allocated;i ++ )
      {
        m_rgArray[i] = pvector_type_traits<_Ty>::default_value();
      }
    }

    bool empty() const
    { // test if sequence is empty
      return (m_nIndex > 0 ? false : true);
    }

    iterator begin()
    { // return iterator for beginning of mutable sequence
      return iterator(&m_rgArray[0]);
    }

    const_iterator begin() const
    {
      return const_iterator(&m_rgArray[0]); 
    }

    iterator end()
    { // return iterator for end of mutable sequence
      return iterator(&m_rgArray[m_nIndex]);
    }

    const_iterator end() const
    {
      return const_iterator(&m_rgArray[m_nIndex]);
    }
    reference operator[](size_type i)
    {
      if (m_nIndex>i)
      {
        return m_rgArray[i];
      }
      else if (i >= allocated)
        {
          resize(i * 2);
        }
        m_nIndex = i + 1;
      return m_rgArray[i];
    } 
    void resize(size_type s)
    {
      value_type * m_rgArray2;
      size_type old_allocated = allocated;
      allocated = s;
      m_rgArray2 = new value_type[allocated];
        //if (allocated>m_nIndex)
        //{
        // m_nIndex=allocated;
       // }
       // cout <<"m_nIndex" << m_nIndex << "allocated" << allocated << endl;
      if (m_nIndex>allocated)
      {
        m_nIndex=allocated;
      }
      for (size_type i = 0;i<m_nIndex;i ++ )
      {
        m_rgArray2[i] = m_rgArray[i];
      }
      delete []m_rgArray;
      m_rgArray = m_rgArray2;
      fill(old_allocated);
    }

    reference back()
    {
      return &m_rgArray[m_nIndex - 1]; 
    }

    const_reference back() const
    {
      return m_rgArray[m_nIndex - 1]; 
    }

    void push_back(const _Ty &_Val)
    { // insert element at end
      if (size() < allocated)
        m_rgArray[m_nIndex ++ ] = _Val;
      else
        {
        resize(allocated * 2);
        m_rgArray[m_nIndex ++ ] = _Val; 
      }
    }

  };
4

11 回答 11

12

它可能是损坏堆栈的缓冲区溢出。如果在运行函数时写入本地定义的缓冲区之外,它可能会覆盖返回地址,然后从函数返回将触发程序崩溃。

您应该寻找使用本地(堆栈分配)变量地址操作的语句 - 它们上的缓冲区溢出最有可能是问题的原因。

于 2009-07-31T09:16:46.117 回答
3

我能想到的可能性:

  • 不同的迭代器 / stl 检查项目之间的调试设置以及它链接到的任何内容。请参阅调试迭代器支持检查迭代器
  • 您的项目和它所链接的任何项目之间的不同 CRT 设置。使用Dependency Walker查看不匹配。
  • 由于函数中的错误代码导致堆栈损坏,例如写入数组或字符串的末尾。
  • 导致堆栈或变量损坏的多线程问题。
  • 不匹配的调用约定(正如你提到的从 Perl 调用它)
于 2009-07-31T09:21:06.763 回答
3

这段代码有很多错误:

  • 命名 - 我已经指出了 的问题_Ty,但为什么有些成员以m_而其他成员不是。一些局部变量也以m_. 不好。

  • 赋值操作不返回引用 - 正如已经指出的那样。

  • 赋值操作有内存泄漏,在第一行,你分配给 m_rgArray,它已经有内容 - 这些被泄露了。

这是初学者。这些都不应该导致崩溃,但它们都需要修复。为了解决所有问题,我会重新开始,一次编写一个函数,并对其进行测试。运行测试,如果成功,编写下一个函数,下一个测试,依此类推。在这样的模板类中投入大量精力是值得的,因为如果它们是正确的,它们会非常有用,但如果错误,则会导致持续的痛苦。

于 2009-07-31T13:29:02.973 回答
2

您是否有任何带有非平凡析构函数的基于堆栈的对象?根据您的调试器,可能很难判断它们何时执行。除了其他评论中提到的所有内容之外,它可能与此相关。

于 2009-07-31T09:31:17.287 回答
1

关于您的运营商的另一条评论=

  • 您没有正确处理自我分配
  • 在我认为您正在泄漏的同一运算符中,我建议您使用 boost::scoped_array 而不是普通指针。

我不知道这是否是给您带来问题的原因,但是由于您似乎遇到了内存损坏问题,因此可能值得关注。

于 2009-07-31T13:29:35.030 回答
1

我假设以下函数有更多代码

int funcname()
{
    return 0; <-- crashing after that...
}

当函数返回时,任何堆栈变量都会调用其析构函数。崩溃很可能发生在其中一个析构函数中。

如何追踪它:

在函数中声明的任何变量的析构函数中放置断点。逐步执行每个析构函数。请记住,当调用析构函数时,会自动调用来自基类的整个析构函数链,并且损坏可能出现在其中任何一个中。

于 2009-07-31T12:46:13.167 回答
0

由于您的详细信息未针对崩溃我建议使用您的 IDE 调试应用程序:在 ProjectProperties->ConfigurationProperties->Debugging 中将命令和命令参数设置为您的 perl / Ultimate++ 应用程序。在调试模式下编译并在您怀疑崩溃的地方放置断点。定位问题并就崩溃本身提出有意义的消息应该相当简单。

于 2009-07-31T12:56:53.600 回答
0

您的赋值运算符应该返回一个引用:

pvector& operator=(const pvector &pv)  {

我怀疑这会导致问题,但试一试。

于 2009-07-31T13:04:08.587 回答
0

在您的函数 (funcname) 中或在 funcname 调用的函数之一中,您可能有一些代码会破坏堆栈。

我遇到的大多数“实践”堆栈损坏是内存副本或字符串副本,当分配的目标缓冲区不适合要复制的数据的大小时(使用 sizeof 和元素数量错误计算大小,不考虑结尾-为字符串复制调整目标缓冲区大小时的字符串终止符)。

另一种不太常见的可能性:指针算法,当引用局部变量或数组并通过该指针更改它们时。

于 2009-07-31T09:21:39.180 回答
0

我的猜测是您的 pvector 在调整大小期间超出了数组的一些问题。我正在尝试阅读代码以了解这是否属实,但我没有看到任何明显的东西。如果您真正想要的只是一个可以增长以适应访问的任何索引的向量,那么您不需要自己编写整个内容。您可以改为扩展 std::vector 并仅使用 reserve()/resize() 方法并让 STL 处理所有复制和内存管理以及迭代器。以下应该有效:

template<typename StoredType>
class pvector : public std::vector<StoredType>
{
public:
    typedef typename std::vector<StoredType, std::allocator<StoredType> >::reference reference;
    typedef typename std::vector<StoredType, std::allocator<StoredType> >::size_type size_type;

    reference at(size_type n)
    {
        size_type need = n+1;
        if(need > std::vector<StoredType>::capacity())
        {
            std::vector<StoredType>::reserve(need * 2);
            std::vector<StoredType>::resize(need);
        }
        else if(need > std::vector<StoredType>::size())
        {
            std::vector<StoredType>::resize(need);
        }
        return std::vector<StoredType>::at(n);
    }

    reference operator[](size_type n)
    {
        return at(n);
    }
};

我在 Linux 上使用 GCC 4.1.2 对其进行了测试,因此希望它也能在 Windows 上编译。

编辑:这是一个不继承的新版本,因为专家似乎同意这不是一个好主意(我学到了一些新东西,耶)。您可以实现所需的其余方法,并直接传递给 m_vector:

template<typename StoredType>
class pvector
{
public:
    typedef StoredType& reference;
    typedef int size_type;

    reference at(size_type n)
    {
        int need = n+1;
        if(need >= m_vector.capacity())
        {
            m_vector.reserve(need * 2);
            m_vector.resize(need);
        }
        else if(need >= m_vector.size())
        {
            m_vector.resize(need);
        }
        return m_vector.at(n);
    }

    reference operator[](size_type n)
    {
        return at(n);
    }

    size_type capacity() { return m_vector.capacity(); }
    size_type size() { return m_vector.size(); }

private:
    std::vector<StoredType> m_vector;
};
于 2009-07-31T13:26:55.610 回答
0

调试器说什么?在返回行之后,它会遍历所有超出范围的对象的析构函数,其中一个几乎可以肯定是搞砸了。因此,在返回行设置一个断点,然后使用调试器单步执行,直到崩溃。

于 2009-07-31T13:15:05.900 回答