Сбой программы при выходе из функции c ++ .... Как вы думаете, что это? - PullRequest
1 голос
/ 31 июля 2009

У меня есть код на 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 вектор ... Его индексы могут быть больше, чем выделенный размер вектора.

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: я думаю, что нашел это. Я использовал утилиту 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; 
      }
    }

  };

Ответы [ 11 ]

12 голосов
/ 31 июля 2009

Это может быть переполнение буфера, повреждающее стек. Если при выполнении функции запись выполняется вне локально определенных буферов, она может перезаписать адрес возврата, а затем возврат из функции вызовет сбой программы.

Вы должны искать операторы, которые работают с адресами локальных (выделяемых стеком) переменных - переполнение буфера для них, скорее всего, является причиной проблемы.

3 голосов
/ 31 июля 2009

В этом коде много неправильного:

  • наименование - я уже указал на проблему с _Ty, но почему некоторые участники начинают с m_, а другие нет. и некоторые локальные переменные также начинаются с m_. Не хорошо.

  • Операция присваивания не возвращает ссылку - как уже указывалось.

  • Операция присваивания имеет утечку памяти, в первой строке вы присваиваете m_rgArray, в котором уже есть содержимое - это утечка.

Это для начала. Ничто из этого не должно вызвать сбой, но все они нуждаются в ремонте. Чтобы исправить все проблемы, я хотел бы начать заново, писать по одной функции за раз, плюс тест для нее. Запустите тест и, если он работает, напишите следующую функцию, следующий тест и так далее. Стоит приложить много усилий к таким шаблонным классам, потому что, если они правы, они очень полезны, но если не правы, то источник постоянной боли.

3 голосов
/ 31 июля 2009

Возможности, которые я могу придумать:

  • Различные итераторы / stl, проверяющие настройки отладки между вашим проектом и всем, на что он ссылается. См. Поддержка отладочного итератора и Проверенный итератор .
  • Различные настройки CRT между вашим проектом и тем, на что он ссылается. Используйте Dependency Walker , чтобы увидеть несоответствия.
  • Повреждение стека из-за неправильного кода в функции, например, запись за концом массива или строки.
  • Проблема многопоточности, приводящая к повреждению стека или переменных.
  • Несоответствие соглашения о вызовах (как вы уже упоминали, вызов из Perl)
2 голосов
/ 31 июля 2009

У вас есть какие-нибудь стековые объекты с нетривиальными деструкторами? В зависимости от вашего отладчика может быть трудно сказать, когда они выполняются. Это может быть что-то связанное с этим, в дополнение ко всему, что упоминается в других комментариях.

1 голос
/ 31 июля 2009

Еще комментарии о вашем операторе =

  • Вы не обрабатываете самостоятельное назначение должным образом
  • В том же операторе, я думаю, что вы просочились, я бы предложил вам использовать boost :: scoped_array вместо обычного указателя.

Я не знаю, из-за того ли это у вас проблем, но из-за того, что у вас проблемы с памятью, это может быть чем-то, с чего можно начать.

1 голос
/ 31 июля 2009

Я предполагаю, что следующая функция содержит больше кода

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

Когда функция вернется, любые переменные стека будут вызывать свои деструкторы. Вполне вероятно, что сбой происходит в одном из деструкторов.

Как отследить это:

Поместите точки останова в деструкторы любых переменных, объявленных в функции. Пройдите через каждого деструктора. Имейте в виду, что когда вызывается деструктор, существует целая цепочка деструкторов из базовых классов, которые будут вызываться автоматически, и повреждение может быть в любом из них.

0 голосов
/ 31 июля 2009

Я полагаю, это проблема с вашим 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);
    }
};

Я протестировал его с GCC 4.1.2 в Linux, так что, надеюсь, он скомпилируется и в Windows.

Edit: вот новая версия, которая не наследуется, так как кажется, что эксперты соглашаются, что это не очень хорошая идея (я узнал что-то новое, ура). Вы можете реализовать остальные необходимые методы и просто передать их в 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;
};
0 голосов
/ 31 июля 2009

Что говорит отладчик? После обратной линии она проходит через все деструкторы для объектов, выходящих из области видимости, и один из них почти наверняка испортился. Поэтому поставьте точку останова на обратной линии, а затем переходите к отладчику, пока не дойдете до сбоя.

0 голосов
/ 31 июля 2009

Ваш оператор присваивания должен возвращать ссылку:

pvector& operator=(const pvector &pv)  {

Я сомневаюсь, что это вызовет проблему, но попробуйте.

0 голосов
/ 31 июля 2009

Поскольку ваши данные не относятся конкретно к сбою, я бы предложил отладить приложение с помощью вашей IDE: В ProjectProperties-> ConfigurationProperties-> Debugging установите команду и аргументы команды в качестве приложений perl / ultimate ++. Скомпилируйте в режиме отладки и установите точки останова там, где вы подозреваете сбой. Должно быть довольно просто определить причину проблемы и получить сообщение о самой аварии.

...