Итак, мне нужна помощь. Я работаю над проектом на C++. Однако я думаю, что мне как-то удалось испортить свою кучу. Это основано на том факте, что я добавил std::string
в класс и присвоил ему значение из другого std::string
:
std::string hello = "Hello, world.\n";
/* exampleString = "Hello, world.\n" would work fine. */
exampleString = hello;
вылетает в моей системе из-за дампа стека. Так что в основном мне нужно остановка, пройтись по всему моему коду и управлению памятью и выяснить, где я напортачил. Кодовая база все еще небольшая (около 1000 строк), так что это легко сделать.
Тем не менее, я не могу себе представить подобные вещи, так что я подумал, что выкину их там. Я нахожусь в системе Linux и ковырялся с valgrind
, и, хотя полностью не знал, что я делаю, он сообщил, что деструктор std::string
был недопустимым бесплатным. Я должен признать, что получил термин «повреждение кучи» из поиска Google; Любые статьи общего назначения на подобные темы также будут оценены.
(До rm -rf ProjectDir
сделайте еще раз на C#: D)
Обновлено: Я не разъяснил это, но я прошу совета по диагностике такого рода проблем с памятью. Я знаю, что std :: string верен, так что это то, что я сделал (или ошибка, но с Select нет проблем). Я уверен, что смогу проверить написанный мной код, и вы, очень умные люди, сразу заметите проблему, но я хочу добавить такой вид анализа кода в свой «набор инструментов», так сказать.
Насколько я могу судить, ваш код правильный. Предполагая, что exampleString - это std :: string, имеющая область класса, как вы описываете, вы должны иметь возможность инициализировать / назначать ее таким образом. Может, есть другая проблема? Может быть, фрагмент реального кода поможет поместить его в контекст.
Вопрос: Является ли exampleString указателем на строковый объект, созданный с помощью new?
Это может быть повреждение кучи, но с такой же вероятностью это повреждение стека. Джим прав. Нам действительно нужно немного больше контекста. Эти две исходные строки мало что говорят нам по отдельности. Это может быть вызвано любым количеством причин (что является настоящей радостью C / C++).
Если вам удобно размещать свой код, вы можете даже выбросить его все на сервер и опубликовать ссылку. Я уверен, что таким образом вы получите гораздо больше советов (некоторые из них, несомненно, не имеют отношения к вашему вопросу).
Ваш код, как я вижу, не содержит ошибок. Как было сказано, требуется больше контекста.
Если вы еще не пробовали, установите gdb (отладчик gcc) и скомпилируйте программу с помощью -g. Это будет компилироваться в отладочные символы, которые может использовать gdb. После установки gdb запустите его с программой (gdb
Установите точку останова для функции, вызывающей ошибку, и посмотрите, каково значение exampleString. Также сделайте то же самое для любого параметра, который вы передаете в exampleString. Это должно хотя бы сказать вам, действительны ли std :: strings.
Я нашел ответ Эта статья хорошим руководством по указателям.
Код был просто примером того, где моя программа терпела неудачу (она была размещена в стеке, Джим). На самом деле я ищу не «что я сделал не так», а скорее «как мне диагностировать, что я сделал не так». Научите человека ловить рыбу и все такое. Хотя, глядя на вопрос, я не дал этого достаточно ясно. Слава богу, за функцию редактирования. : ')
Кроме того, я исправил проблему с std :: string. Как? Заменив его вектором, скомпилировав, а затем снова заменив строку. был постоянно дает сбой, и это исправлено, хотя ... не могло. Там есть что-то противное, и я не знаю, что именно. Я действительно хотел проверить один раз, когда я вручную выделяю память в куче:
this->map = new Area*[largestY + 1];
for (int i = 0; i < largestY + 1; i++) {
this->map[i] = new Area[largestX + 1];
}
и удалив его:
for (int i = 0; i < largestY + 1; i++) {
delete [] this->map[i];
}
delete [] this->map;
Раньше я не выделял 2d-массив с помощью C++. Вроде работает.
О, если вы хотите знать, как отладить проблему, это просто. Сначала возьми дохлого цыпленка. Затем начни это трясти.
Серьезно, я не нашел последовательного способа отследить подобные ошибки. Поскольку существует так много потенциальных проблем, нет простого контрольного списка, который нужно пройти. Однако я бы порекомендовал следующее:
exampleString = hello;
.exampleString = hello;
, а не при выходе из какого-либо закрывающего блока (что может привести к срабатыванию деструкторов).Есть много других вещей, которые можно попробовать. Я уверен, что некоторые другие люди тоже присоединятся к своим идеям.
Also, I actually fixed the std::string problem. How? By replacing it with a vector, compiling, then replacing the string again. It was consistently crashing there, and that fixed even though it...couldn't. There's something nasty there, and I'm not sure what.
Похоже, вы действительно трясли курицу. Если вы не знаете, что Почему работает сейчас, значит, он все еще сломан и почти наверняка укусит вас позже (после того, как вы добавите еще больше сложности).
Некоторые места для начала:
Если вы работаете в Windows и используете Visual C++ 6 (я надеюсь, что никто не использует его в наши дни), реализация std :: string не является потокобезопасной и может привести к подобным вещам.
На моем предыдущем рабочем месте мы использовали Compuware Boundschecker, чтобы помочь в этом. Это коммерчески и очень дорого, поэтому не может быть вариантом.
Вот пара бесплатных библиотек, которые могут пригодиться
http://www.codeguru.com/cpp/misc/misc/memory/article.php/c3745/
http://www.codeproject.com/KB/cpp/MemLeakDetect.aspx
Надеюсь, это поможет. Повреждение памяти - отстойное место!
Запустите Purify.
Это почти волшебный инструмент, который сообщит, когда вы забиваете память, к которой не должны прикасаться, утечку памяти, не освобождая вещи, двойное освобождение и т. д.
Он работает на уровне машинного кода, поэтому вам даже не нужно иметь исходный код.
Один из самых приятных конференц-звонков с поставщиками, в котором я когда-либо участвовал, состоялся, когда Purify обнаружили утечку памяти в их коде, и мы смогли спросить: «Возможно, вы не освобождаете память в своей функции foo ()» и услышали изумление в их голосах.
Они думали, что мы отлаживаем богов, но потом мы открыли им секрет, чтобы они могли запустить Purify до того, как нам пришлось использовать их код. :-)
http://www-306.ibm.com/software/awdtools/purify/unix/
(Это довольно дорого, но у них есть бесплатная пробная версия)
Один из методов отладки, который я часто использую (за исключением крайних странностей), - это разделять и властвовать. Если ваша программа в настоящее время не работает с какой-либо конкретной ошибкой, разделите ее пополам и посмотрите, есть ли в ней та же ошибка. Очевидно, фокус в том, чтобы решить, на что разделить вашу программу!
В приведенном вами примере недостаточно контекста, чтобы определить, где может быть ошибка. Если бы кто-нибудь еще попробовал ваш пример, он бы сработал. Итак, в своей программе попробуйте удалить как можно больше лишних вещей, которые вы нам не показывали, и посмотрите, работает ли это тогда. Если это так, то постепенно добавляйте другой код, пока он не начнет давать сбой. Тогда, вероятно, проблема в том, что вы только что добавили.
Обратите внимание: если ваша программа многопоточная, у вас, вероятно, есть более серьезные проблемы. Если нет, то вы сможете сузить круг таким образом. Удачи!
Это относительно дешевые механизмы для возможного решения проблемы:
new[]
и delete[]
, но вы уже это делаете.assert()
. Откуда я знаю это, не видя этого? Как и в случае с зубной нитью, ни у одного assert()
нет достаточного количества кода. Добавьте функцию проверки для своих объектов и вызовите ее при запуске и завершении метода.auto_ptr
! Это ... удивительно; его семантика очень странная. Вместо этого выберите один из Повысьте умные указатели или что-то из библиотека Локи.В наши дни C++ имеет свои собственные интеллектуальные указатели в стандартной библиотеке, поэтому для этого не нужны Boost или Loki.
Помимо таких инструментов, как Boundschecker или Purify, лучше всего при решении подобных проблем просто хорошо научиться читать код и ознакомиться с кодом, над которым вы работаете.
Повреждение памяти - одна из самых сложных вещей для устранения неполадок, и обычно эти типы проблем решаются путем проведения часов / дней в отладчике и замечаний чего-то вроде «эй, указатель X используется после того, как он был удален!».
Если это кому-то поможет, то с приобретением опыта вы станете лучше.
Выделение памяти для массива выглядит правильно, но не забудьте также проверить все места, где вы обращаетесь к массиву.
Однажды у нас была ошибка, которая ускользнула от всех обычных методов, valgrind, purify и т. д. Сбой случился только на машинах с большим объемом памяти и только с большими наборами входных данных.
В конце концов мы отследили его, используя точки наблюдения отладчика. Постараюсь описать процедуру здесь:
1) Найдите причину неисправности. Из вашего примера кода видно, что память для "exampleString" повреждена и поэтому не может быть записана. Продолжим это предположение.
2) Установите точку останова в последнем известном месте, в котором exampleString используется или изменяется без каких-либо проблем.
3) Добавьте точку наблюдения к элементу данных exampleString. В моей версии g ++ строка хранится в _M_dataplus._M_p
. Мы хотим знать, когда изменяется этот член данных. Методика GDB для этого:
(gdb) p &exampleString._M_dataplus._M_p
= (char **) 0xbfccc2d8
(gdb) watch *
Hardware watchpoint 1: *
Я, очевидно, использую Linux с g ++ и gdb здесь, но я считаю, что точки наблюдения за памятью доступны в большинстве отладчиков.
4) Продолжайте, пока не сработает точка наблюдения:
Continuing.
Hardware watchpoint 2: *
Old value = 0xb7ec2604 ""
New value = 0x804a014 ""
0xb7e70a1c in std::string::_M_mutate () from /usr/lib/libstdc++.so.6
(gdb) where
Команда gdb where
даст обратную трассировку, показывающую, что привело к модификации. Это либо совершенно законная модификация, и в этом случае просто продолжайте, либо, если вам повезет, это будет модификация из-за повреждения памяти. В последнем случае вы должны теперь иметь возможность просмотреть код, который В самом деле вызывает проблему, и, надеюсь, исправить ее.
Причиной нашей ошибки был доступ к массиву с отрицательным индексом. Индекс был результатом преобразования указателя на int по модулю размера массива. Ошибка была пропущена valgrind et al. поскольку адреса памяти, выделенные при работе с этими инструментами, никогда не были «> MAX_INT
» и поэтому никогда не приводили к отрицательному индексу.
Отличное обсуждение для Linux! Скучаю по развитию в этой среде. Мне нужно решение для WinDoze ... (VS6.0 тоже) ... (не моя вина! Клиенты используют VS6.0 и клиенты всегда правы :).
+1, хороший список! Тем не менее, я бы оспорил №8 - хотя он предотвращает «плохой» доступ, на самом деле это запах кода, который, по моему опыту, скрывает плохую логику или плохое управление временем жизни объекта ...