2

C++ コードがあり、MSC9 を使用してコンパイルします。ランダムにクラッシュし続けます。たとえば、Perl から `` を使用して呼び出すとクラッシュしますが、コマンド ラインまたは Ultimate++ から呼び出すとクラッシュしません。

私はそれをperlから呼び出すことを意味します。f.exe arg1 arg2 arg3

スタック トレースはあまり表示されません。プログラムを1行ずつトレースすると、プログラムが最後に戻ったときに失敗することが証明されました...

だからその通りです

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 互換のベクトルです... そのインデックスは、ベクトルの割り当てられたサイズよりも大きくなる可能性があります。

Update1: 割り当てに漏れがあるという提案を受け取りました。割り当てを変更しました...それが今の様子です:

 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: 見つかったと思います。gflags.exe および windbg.exe と呼ばれる Microsoft デバッグ ツールのユーティリティを使用して、正確な場所を見つけました。gflags.exe /p /enable myprog.exe /full を使用して、ヒープ バグの例外を有効にしました。ここで、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

このコードには多くの問題があります:

  • 命名 - で問題を指摘しました_Tyが、なぜ一部のメンバーは で始まりm_、他のメンバーはそうではないのでしょうか。一部のローカル変数も で始まりますm_。良くない。

  • 割り当て op は参照を返しません - すでに指摘したように。

  • 代入 op にはメモリ リークがあります。最初の行で、既にコンテンツを持っている m_rgArray に代入します - これらはリークされています。

それは初心者向けです。これらのいずれもクラッシュの原因にはなりませんが、すべて修正する必要があります。すべての問題を解決するには、もう一度やり直して、一度に 1 つの関数を作成し、そのテストを行います。テストを実行し、それが機能する場合は、次の関数、次のテストなどを記述します。このようなテンプレート クラスに多大な努力を払う価値はあります。なぜなら、それらが正しければ非常に便利ですが、間違っていれば継続的な苦痛の原因となるからです。

于 2009-07-31T13:29:02.973 に答える
1

次の関数にはさらに多くのコードがあると想定しています

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

関数が戻ると、スタック変数のデストラクタが呼び出されます。デストラクタの 1 つでクラッシュが発生している可能性があります。

それを追跡する方法:

関数で宣言された変数のデストラクタにブレークポイントを配置します。各デストラクタをステップ実行します。デストラクタが呼び出されると、基本クラスから自動的に呼び出されるデストラクタのチェーン全体が存在し、それらのいずれかで破損が発生する可能性があることに注意してください。

于 2009-07-31T12:46:13.167 に答える
1

オペレーターに関する別のコメント=

  • 自己割り当てを適切に処理していません
  • 漏れていると思う同じ演算子で、通常のポインターの代わりにboost::scoped_arrayを使用することをお勧めします。

これが問題を引き起こしているのかどうかはわかりませんが、メモリ破損の問題があるように見えるため、スターになる可能性があります.

于 2009-07-31T13:29:35.030 に答える
0

代入演算子は参照を返す必要があります。

pvector& operator=(const pvector &pv)  {

それが問題の原因になるとは思えませんが、試してみてください。

于 2009-07-31T13:04:08.587 に答える
0

あなたの詳細はクラッシュに固有のものではないので、IDE を使用してアプリケーションをデバッグすることをお勧めします。デバッグ モードでコンパイルし、クラッシュが疑われる場所にブレークポイントを設定します。問題を特定し、クラッシュ自体に関する意味のあるメッセージを表示するのは非常に簡単です。

于 2009-07-31T12:56:53.600 に答える
0

デバッガーは何と言っていますか? 戻り行の後、スコープ外に出るオブジェクトのすべてのデストラクタを実行しますが、そのうちの 1 つがほぼ確実に失敗しています。そのため、リターン行にブレークポイントを置き、クラッシュするまでデバッガーをステップ実行します。

于 2009-07-31T13:15:05.900 に答える
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 に答える