Конструктор вызван с неправильным указателем «this».Это повреждение стека? - PullRequest
6 голосов
/ 01 марта 2012

edit: Я понял это с помощью комментаторов.Чтобы ответить на вопрос, поставленный в моем заголовке: Нет, это не повреждение стека, его GDB сообщает неверные значения.Программа на самом деле ведет себя как ожидалось и имеет правильный указатель this.Фактическое поведение с ошибками, которое побудило меня опубликовать этот вопрос, вероятно, совершенно не связано с проблемой, которую я описываю здесь.

Сначала предупреждение.Я полагаю, что это проблема повреждения памяти, и я, как правило, не ожидаю ответа, за исключением «тщательно проверить свой код», но я неоднократно видел это поведение и надеялся, что некоторые из вас поняли проблему и как яМожно найти его источник.

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

itvt::itvt(const itvt& i)
  : _i(i.type == INTBV ? new intbv_intervalt(i.i()) : NULL),
    _f(i.type == FLOAT ? new float_intervalt(i.f()) : NULL),
    type(i.type), other_bottom(i.other_bottom) 
{ }

Теперь я обнаружил ошибку повреждения памяти и смог отследить ее до следующего фрагмента кода:

itvt itvt::get_split(bool le) const
{
   itvt result(*this);
   [...]
}

Используя gdb, я обнаружил, что вызов конструктора, по-видимому, не создает объект «result»:

Breakpoint 1, itvt::get_split (this=0x1016af560, le=false) at itv.cpp:517
517   itvt result(*this);
(gdb) n
519   if(is_singleton() || is_bot())
(gdb) print result
$3 = {
  _i = {
    _M_ptr = 0x7fff5fbfe100
  }, 
  _f = {
    _M_ptr = 0x7fff5fbfed60
  }, 
  type = 1606410016, 
  other_bottom = 255
}
(gdb) print *this
$4 = {
  _i = {
    _M_ptr = 0x1020833a0
  }, 
  _f = {
    _M_ptr = 0x0
  }, 
  type = itvt::INTBV, 
  other_bottom = false
}

Если заглянуть глубже, я обнаружил, что внутри конструктора копирования указатель «this» указывает нанеправильный объект:

Breakpoint 1, itvt::get_split (this=0x1016af560, le=false) at itv.cpp:517
517   itvt result(*this);
(gdb) print &result
$5 = (itvt *) 0x7fff5fbfdee0
(gdb) s     
itvt::itvt (this=0x7fff5fbfdf80, i=@0x1016af560) at itv.cpp:500
500     type(i.type), other_bottom(i.other_bottom)
(gdb) print this
$6 = (itvt * const) 0x7fff5fbfdf80

Поскольку «стек» расположен в стеке по адресу 0x7fff5fbfdee0, я ожидаю, что указатель «this» внутри конструктора копирования будет указывать на тот же адрес.Вместо этого он указывает на 0x7fff5fbfdf80.

Похоже, что конструктор копирования инициализирует что-то , но не объект "результата" в стеке, для которого он вызывается.Фактически, я могу получить доступ к области памяти, которую конструктор инициализировал на отлично:

Breakpoint 1, itvt::get_split (this=0x1016af560, le=false) at itv.cpp:517
517   itvt result(*this);
(gdb) s    
itvt::itvt (this=0x7fff5fbfdf80, i=@0x1016af560) at itv.cpp:500
500     type(i.type), other_bottom(i.other_bottom)
(gdb) finish
Run till exit from #0  itvt::itvt (this=0x7fff5fbfdf80, i=@0x1016af560) at itv.cpp:500
itvt::get_split (this=0x1016af560, le=false) at itv.cpp:519
519   if(is_singleton() || is_bot())
(gdb) print *((const itvt*) (0x7fff5fbfdf80))
$7 = {
  _i = {
    _M_ptr = 0x1016b6d10
  }, 
  _f = {
    _M_ptr = 0x0
  }, 
  type = itvt::INTBV, 
  other_bottom = false
}

Мой первый вопрос: Может ли тот факт, что указатель "this" указывает на неправильный объект, бытьобъяснил как нормальное поведение?Кажется, какая-то странная проблема с повреждением памяти, но, может быть, я что-то упустил.

Я скомпилировал с флагами g ++ и "-O0 -ggdb" и сделал новую перекомпиляцию всего между прочим.Вот моя версия g ++:

leo@scythe ai$ g++ --version
i686-apple-darwin11-llvm-g++-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Мой второй вопрос: Если это повреждение памяти, у вас есть какие-либо советы о том, как я могу отследить источник.Я обычно отслеживаю такие проблемы по их основной причине, используя gdb, но я не знаю, где искать сейчас.

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

Я благодарен за любые советы или советы, которые вы можете предложить.

edit: Вот соответствующий фрагмент класса itvt:

class itvt
{
protected:
  typedef std::auto_ptr<intbv_intervalt> iptrt;
  typedef std::auto_ptr<float_intervalt> fptrt;
  iptrt _i;
  fptrt _f;
public:
  typedef enum {INTBV, FLOAT, OTHER} itv_typet;
  itv_typet type;

  bool other_bottom;

  //copy constr
  itvt(const itvt& i);

  inline intbv_intervalt& i() { return *_i; }
  inline float_intervalt& f() { return *_f; }
  inline const intbv_intervalt& i() const { return *_i; }
  inline const float_intervalt& f() const { return *_f; }

  itvt get_split(bool le) const;

  [...]
};

Ответы [ 2 ]

4 голосов
/ 01 марта 2012

Второй вопрос: используйте valgrind , это действительно то, что вам нужно здесь.Он отслеживает каждое выделение / освобождение и сообщает вам, пытаетесь ли вы использовать освобожденную память, а также множество других вещей.Это покажет вам повреждение памяти.

2 голосов
/ 02 марта 2012

Я понял это с помощью комментаторов: похоже, GDB не говорит правду (возможно, из-за старой версии GDB).Программа на самом деле ведет себя так, как и ожидалось.

Фактическое поведение с ошибками, которое побудило меня взглянуть на проблему, было совершенно не связано с инициализацией объекта.

...