Zarządzania pamięcią, a także C++

Potrzebuję pomocy. Pracuję nad projektem w C++. Jednak myślę, że jakoś udało mi się zepsuć moją stertę. Jest to oparte na fakcie, że dodałem std::string do klasy i przypisałem jej wartość z innego std::string:

std::string hello = "Hello, world.\n";
/* exampleString = "Hello, world.\n" would work fine. */
exampleString = hello;

Zawiesza się w moim systemie zrzutem stosu. Więc muszę przestać, przejrzeć cały mój kod i zarządzanie pamięcią i dowiedzieć się, gdzie schrzaniłem. Baza kodowa jest nadal mała (około 1000 linii), więc jest to łatwe do zrobienia.

Mimo to, mam tego za dużo, więc pomyślałem, że to wyrzucę. Jestem na Linuksie i grzebałem z valgrind, i chociaż nie wiedząc do końca, co robię, donosiło, że Destruktor std::string jest nieważnym wolnym. Muszę przyznać, że słowo "korupcja sterty" pochodzi z wyszukiwarki Google; wszelkie artykuły ogólnego przeznaczenia dotyczące tego rodzaju rzeczy byłyby również mile widziane.

(przed rm -rf ProjectDir, zrób jeszcze raz w C#: D)

Edytuj: Ja nie jasne, ale proszę o radę w diagnozowaniu tego typu problemów z pamięcią. Wiem, że std:: string ma rację, więc to coś, co zrobiłem (lub błąd, ale nie ma problemu z Select). Jestem pewien, że mógłbym sprawdzić kod, który napisałem, a Wy bardzo mądrzy ludzie zobaczycie problem w krótkim czasie, ale chcę dodać tego rodzaju analizę kodu do mojego "toolbox", jakby to było.

Author: sameera lakshitha, 2008-08-11

12 answers

Są to stosunkowo tanie mechanizmy ewentualnego rozwiązania problemu:

  1. miej oko na moje pytanie o korupcję - aktualizuję odpowiedzi, gdy się trzęsą. Pierwszym było balansowanie new[] i delete[], ale już to robisz.
  2. Give valgrind more of a go; jest to doskonałe narzędzie, i żałuję tylko, że nie było dostępne pod Windows. Ja tylko spowalnia twój program o połowę, co jest całkiem dobre w porównaniu do Windowsa / align = "left" /
  3. pomyśl o użyciu Google Performance Tools jako zamiennika / nowego.
  4. czy wyczyściłeś wszystkie pliki obiektowe i zacząłeś od nowa? Być może Twój plik make jest... "suboptimal"
  5. Nie jesteś wystarczająco w swoim kodzie. Skąd mam to wiedzieć, nie widząc tego? Podobnie jak nić dentystyczna, nikt nie ma wystarczająco dużo w swoim kodzie. Dodaj funkcję walidacji dla swoich obiektów i wywołaj ją przy metodzie start i method end.
  6. Are you compiling-wall ? Jeśli nie, zrób to.
  7. Znajdź sobie narzędzie lint jak PC-Lint . Mała aplikacja jak twoja może zmieścić się na stronie PC-lint demo , co oznacza, że nie ma zakupu dla Ciebie!
  8. sprawdź, czy anulujesz wskaźniki po ich usunięciu. Nikt nie lubi zwisającego wskaźnika. Ten sam koncert z zadeklarowanymi, ale nieprzydzielonymi wskaźnikami.
  9. przestań używać tablic. Zamiast tego użyj wektora .
  10. nie używaj surowych wskaźników. Użyj inteligentnego wskaźnika . Nie używaj auto_ptr! To coś jest... zaskakujące; jego semantyka jest bardzo dziwna. Zamiast tego wybierz jeden z Boost smart pointers, lub coś z biblioteki Loki.
 21
Author: Josh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:29:25

Mieliśmy kiedyś błąd, który wymykał się regularnym technikom, valgrindowi, oczyszczaniu itp. Awaria miała miejsce tylko na maszynach z dużą ilością pamięci i tylko na dużych zestawach danych wejściowych.

Ostatecznie wyśledziliśmy go za pomocą punktów obserwacyjnych debuggera. Postaram się opisać procedurę tutaj:

1) znajdź przyczynę awarii. Z przykładowego kodu wynika, że pamięć dla "exampleString" jest uszkodzona, więc nie można jej zapisać. Kontynuujmy to. założenie.

2) Ustaw punkt przerwania w ostatnim znanym miejscu, w którym "exampleString" jest używany lub modyfikowany bez żadnego problemu.

3) Dodaj punkt obserwacyjny do elementu danych 'exampleString'. W mojej wersji g++ ciąg jest przechowywany w _M_dataplus._M_p. Chcemy wiedzieć, kiedy ten członek Danych się zmieni. Technika GDB to:

(gdb) p &exampleString._M_dataplus._M_p
$3 = (char **) 0xbfccc2d8
(gdb)  watch *$3
Hardware watchpoint 1: *$3

Oczywiście używam Linuksa z g++ i gdb tutaj, ale wierzę, że Punkty memory watch są dostępne z większością debugerów.

4) Kontynuuj do momentu uruchomienia punktu obserwacyjnego:

Continuing.
Hardware watchpoint 2: *$3

Old value = 0xb7ec2604 ""
New value = 0x804a014 ""
0xb7e70a1c in std::string::_M_mutate () from /usr/lib/libstdc++.so.6
(gdb) where

Komenda gdb where wyświetli ślad wstecz pokazujący, co spowodowało modyfikację. Jest to albo całkowicie legalna modyfikacja, w takim przypadku po prostu kontynuuj - lub jeśli masz szczęście, będzie to modyfikacja z powodu uszkodzenia pamięci. W tym drugim przypadku, powinieneś teraz być w stanie przejrzeć kod, który jest naprawdę powoduje problem i miejmy nadzieję, że go naprawi.

Przyczyną naszego błędu był dostęp do tablicy z indeks ujemny. Indeks był wynikiem oddania wskaźnika do " int " modulos wielkości tablicy. Błąd został pominięty przez valgrind et al. ponieważ adresy pamięci przydzielane podczas pracy pod tymi narzędziami nigdy nie były "> MAX_INT " i nigdy nie powodowały ujemnego indeksu.

 10
Author: Richard Corden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 13:47:32

Jeśli chcesz wiedzieć, jak debugować problem, to proste. Najpierw kup martwego kurczaka. Następnie, Zacznij Potrząsać .

Poważnie, nie znalazłem spójnego sposobu, aby wyśledzić tego rodzaju błędy. Ponieważ jest tak wiele potencjalnych problemów, nie ma prostej listy kontrolnej do przejścia. Polecam jednak:
  1. rozgość się w debuggerze.
  2. rozpocznij przeglądanie debugera, aby zobaczyć, czy możesz znaleźć coś, co wygląda fishy. Sprawdź szczególnie, co dzieje się podczas linii exampleString = hello;.
  3. upewnij się, że rzeczywiście rozbija się na linii exampleString = hello;, a nie podczas wychodzenia z jakiegoś zamkniętego bloku (co może spowodować odpalenie destruktorów).
  4. Sprawdź magię wskazówkową, którą możesz wykonywać. Arytmetyka wskaźników, odlewanie itp.
  5. sprawdź wszystkie alokacje i dealokacje, aby upewnić się, że są one dopasowane (bez podwójnych dealokacji).
  6. upewnij się, że nie zwracasz żadnych referencji lub wskaźniki do obiektów na stosie.
Jest też wiele innych rzeczy do wypróbowania. Jestem pewien, że inni też będą się zastanawiać nad pomysłami.
 7
Author: Derek Park,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-11 06:20:21

Kilka miejsc na początek:

Jeśli jesteś na Windowsie i używasz visual C++6 (mam nadzieję, że w dzisiejszych czasach nikt go jeszcze nie używa) to wstawianie std:: string nie jest threadsafe i może prowadzić do tego typu rzeczy.

Oto artykuł, który znalazłem, który wyjaśnia wiele typowych przyczyn wycieków pamięci i korupcji.

W moim poprzednim miejscu pracy użyliśmy Compuware Boundschecker, aby pomóc w tym. Jest komercyjny i bardzo drogi, więc może nie być opcja.

Oto kilka darmowych bibliotek, które mogą się przydać

Http://www.codeguru.com/cpp/misc/misc/memory/article.php/c3745/

Http://www.codeproject.com/KB/cpp/MemLeakDetect.aspx

Mam nadzieję, że to pomoże. Korupcja pamięci to beznadziejne miejsce!

 3
Author: Orion Edwards,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-11 06:49:15

To może być korupcja sterty, ale równie prawdopodobne jest korupcja stosu. Jim ma rację. Naprawdę potrzebujemy trochę więcej kontekstu. Te dwa źródła nie mówią nam wiele w izolacji. Może być wiele rzeczy powodujących to (co jest prawdziwą radością C / C++).

Jeśli wygodnie Ci wrzucać swój kod, możesz nawet wrzucić go na serwer i zamieścić link. Jestem pewien, że dostaniesz w ten sposób o wiele więcej porad (niektóre z nich niewątpliwie nie mają związku z twoim pytaniem).

 1
Author: Derek Park,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-11 05:31:27

Kod był po prostu przykładem, gdzie mój program zawodził (został przydzielony na stos, Jim). Nie szukam "co zrobiłem źle", ale raczej "jak zdiagnozować, co zrobiłem źle". Naucz człowieka łowić ryby i tak dalej. Chociaż patrząc na pytanie, nie wyraziłem się wystarczająco jasno. Dzięki Bogu za funkcję edycji. :')

Naprawiłem również problem ze std:: string. Jak? Poprzez zastąpienie go wektorem, kompilację, a następnie ponowne zastąpienie łańcucha. To było konsekwentnie się tam rozbijało, i to naprawiło, mimo że to...jest tam coś paskudnego i nie wiem co. Chciałem jednak sprawdzić, kiedy ręcznie przydzielam pamięć na stercie:

 this->map = new Area*[largestY + 1];
 for (int i = 0; i < largestY + 1; i++) {
     this->map[i] = new Area[largestX + 1];
 }

I usunięcie go:

for (int i = 0; i < largestY + 1; i++) {
    delete [] this->map[i];
}
delete [] this->map;

Nie przydzielałem wcześniej tablicy 2d z C++. Wygląda na to, że działa.

 1
Author: Bernard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-11 06:16:46

Naprawiłem również problem ze std:: string. Jak? Poprzez zastąpienie go wektorem, kompilację, a następnie ponowne zastąpienie łańcucha. Ciągle się tam rozbijał, a to naprawiało, mimo to...jest tam coś paskudnego i nie wiem co.

To brzmi, jakbyś naprawdę potrząsał kurczakiem. Jeśli nie wiesz Dlaczego to działa teraz, to nadal jest zepsuty i prawie gwarantowane, że ugryzie cię ponownie później (po dodaniu nawet więcej złożoności).
 1
Author: Derek Park,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-11 06:26:06

Run Purify.

Jest to prawie magiczne narzędzie, które zgłosi, gdy zatykasz pamięć, której nie powinieneś dotykać, wyciekając pamięć, nie uwalniając rzeczy, podwójnie uwalniając itp.

Działa na poziomie kodu maszynowego, więc nawet nie musisz mieć kodu źródłowego.

Jedna z najbardziej przyjemnych rozmów konferencyjnych między sprzedawcami, jakie kiedykolwiek odbyłem, była wtedy, gdy Purify znalazła wyciek pamięci w ich kodzie i byliśmy w stanie zapytać: "czy to możliwe, że nie zwalniasz pamięci w swojej funkcji foo () " i usłyszeć zdumienie w ich głosach.

Myśleli, że debugujemy bogów, ale potem ujawniliśmy im tajemnicę, żeby mogli uruchomić Purify, zanim będziemy musieli użyć ich kodu. :-)

Http://www-306.ibm.com/software/awdtools/purify/unix/

(jest dość drogie, ale mają darmowy eval do pobrania)

 1
Author: Mark Harrison,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-11 07:24:10

Jedną z technik debugowania, których często używam (z wyjątkiem przypadków najbardziej ekstremalnych dziwności) jest dzielenie i podbijanie. Jeśli twój program zawiedzie z jakimś konkretnym błędem, podziel go w jakiś sposób na pół i sprawdź, czy nadal ma ten sam błąd. Oczywiście sztuczka polega na tym, aby zdecydować, gdzie podzielić swój program!

Twój przykład nie pokazuje wystarczająco kontekstu, aby określić, gdzie może być błąd. Gdyby ktoś inny spróbował twojego przykładu, to by się udało. Więc, w swoim programie spróbuj usunąć jak najwięcej dodatkowych rzeczy, których nam nie pokazałeś i sprawdź, czy to działa. Jeśli tak, to dodaj drugi kod z powrotem trochę na raz, aż zacznie zawodzić. Więc to, co właśnie dodałeś, jest prawdopodobnie problemem.

Zauważ, że jeśli twój program jest wielowątkowy, to prawdopodobnie masz większe problemy. Jeśli nie, powinieneś być w stanie zawęzić to w ten sposób. Powodzenia!

 1
Author: Greg Hewgill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-11 08:34:56

Poza narzędziami takimi jak Boundschecker czy Purify, najlepszym rozwiązaniem w rozwiązywaniu takich problemów jest po prostu naprawdę dobre czytanie kodu i zapoznanie się z kodem, nad którym pracujesz.

Uszkodzenie pamięci jest jedną z najtrudniejszych rzeczy do rozwiązania i zazwyczaj tego typu problemy są rozwiązywane przez spędzanie godzin / dni w debuggerze i zauważanie czegoś w stylu " Hej, wskaźnik X jest używany po jego usunięciu!".

Jeśli to pomoże, to coś dostaniesz lepiej w miarę zdobywania doświadczenia.

Twój przydział pamięci dla tablicy wygląda poprawnie, ale upewnij się, że sprawdzisz również wszystkie miejsca, w których masz dostęp do tablicy.

 1
Author: 17 of 26,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-21 19:18:44

Twój kod jak widzę nie ma błędów. Jak już powiedziano, potrzebny jest większy kontekst.

Jeśli jeszcze nie próbowałeś, zainstaluj gdb (debugger gcc) i skompiluj program za pomocą-g. będzie to skompilowane w symbolach debugowania, których gdb może używać. Po zainstalowaniu gdb uruchom go z programem (gdb). to jest przydatnym cheatsheatem do używania gdb.

Ustaw punkt przerwania dla funkcji, która generuje błąd i zobacz jaka jest wartość exampleString. Również zrobić to samo dla niezależnie od parametru, który PRZEKAZUJESZ do exampleString. To powinno ci przynajmniej powiedzieć, czy STD:: strings są poprawne.

Znalazłem odpowiedź z Ten artykuł Być dobrym przewodnikiem o pointers.

 1
Author: roo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:29

Z tego co wiem Twój kod jest poprawny. Zakładając, że exampleString jest ciągiem std::, który ma zakres klasy, tak jak opisujesz, powinieneś być w stanie zainicjalizować/przypisać go w ten sposób. Może jest jakiś inny problem? Może fragment kodu pomoże umieścić go w kontekście.

Pytanie: czy exampleString jest wskaźnikiem do obiektu string utworzonego za pomocą new?

 0
Author: JimDaniel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-11 05:26:12