[solved] aplikacja Qt GUI - "upływ" danych?
-
Cześć.
Mam do napisania aplikację okienkową, która kataloguje zdjęcia według różnych parametrów exif. Polega to na tym, że użytkownik podaje lokalizację folderu ze zdjęciami, program odczytuje wybrane dane, tworzy z nich listę plików i wyświetla. Istnieje możliwość sortowania po każdym parametrze, rosnąco lub malejąco. Program uruchamia się, wyświetla listę plików poprawnie, pierwsze sortowanie też działa, dalej ilość pozycji się zmniejsza, aż w końcu program przestaje działać. Czasem działaja jedno sortowanie, czasem kilka. Efekt końcowy jest zawsze taki sam. Upływ danych i zawieszenie się aplikacji - czasem od razu się zawiesza. Co robię źlę?Link do plików z programem:
"program exiv":https://docs.google.com/file/d/0B9NtEembQQvWOHJVcXhEZFlEeGM/edit?usp=sharingDziękuję za pomoc :)
-
Użyj valgrinda, lub nawet zwykłego debuggera żeby zobaczyć gdzie tracisz pamięć. Prawdopodobnie gdzieś coś mieszasz we wskaźnikach.
-
Zdefiniuj "coś jest nie tak". Ustawianie wskaźnika na NULL na pewno nie jest złe samo w sobie, ale z reguły wcześniej dobrze jest zniszczyć ten obiekt (delete). To jednak bardzo zależy od implementacji (czasem dany wskaźnik jest tylko lokalny i nie ma potrzeby niszczyć obiektu, albo jest to QObjekt i niszczeniem zajmie się meta object system).
-
Ale skoro zniszczę obiekt to później nie będę mógł się już odwołać do tego miejsca w liście i program praktycznie w ogóle nie działa. No, chyba, że coś mylę...
Gdy przestaje działać pojawia się komunikat: Otrzymano sygnał. Podproces zatrzymany, ponieważ otrzymał on sygnał z systemu operacyjnego. Nazwa sygnału: SIGSEGV. Znaczenie sygnału: Segmentation fault.
Kompilator przenosi mnie do modułu sortowanie.cpp i wskazuje żółtą strzałką na linijkę 92, czyli: poprzedni->next = NULL;
Jeśli zmienię wątek, to wskazuje na coś takiego: 0x77a700fd <+0x02d3> sub $0x14,%esp
Jeśli przełączę w tryb debugowania na instrukcjach:
@
92 poprzedni->next = NULL;
0x4035f4 <+0x011f> mov -0x1c(% ebp),% eax
0x4035f7 <+0x0122> movl $0x0,0x38(% eax)@(Błąd w pogrubionej linijce, też wskazywany przez żółtą strzałkę.)
Wcześniej napisałem aplikację konsolową i wszystko było ok, teraz po podlączeniu nie działa.
bq. niszczeniem zajmie się meta object system
Chodzi o destruktor?
-
Ok czyli jesteś w sytuacji gdzie nie trzeba nic niszyczyć ;) Nie zaglądam do twojego kodu, także w gruncie rzeczy zgaduję co się dzieje.
Jeśli tylko poprzedni->next nie jest prywatny (a nie jest, bo by cię kompilator zakrzyczał), to NULL zawsze ustawić możesz. Sprawdź czy gdzieś potem w kodzie nie próbujesz odczytać tego wskaźnika: system wykrzaczy się SIGSEGV właśnie wtedy, gdy próbujesz dostać się do zmiennej wskazywanej przez pointer, a tam nic nie ma (NULL).
Czyli jeśli dobrze rozumiem, to w debugowaniu program się krzaczy, ale w release działa, tylko ubywa pozycji na liście? Może ubywa dlatego że kasujesz obiekty "next" właśnie?
-
W trybie release (to ten zwykły przysisk play, ctrl+R, zgadza się?) program też w pewnym momencie przestaje działac i wtedy wywala "Program exif_okienkowo.exe przestał działać".
To moja funkcja sortująca:
@lista * sortuj_rosnaco(lista * akt_lst, int pole){
lista * max, *poczatek, *tmp_poczatek, *poprzedni;
int ten_sam;
max = akt_lst;
poczatek = akt_lst;
tmp_poczatek = akt_lst;while(tmp_poczatek->next!=NULL){ max = tmp_poczatek; akt_lst = tmp_poczatek; ten_sam = 1; while (akt_lst!=NULL){ if (porownaj_elementy(max, akt_lst, pole)==1){ max = akt_lst; ten_sam--; } akt_lst = akt_lst->next; } if (ten_sam == 1){ if(tmp_poczatek != poczatek){ tmp_poczatek = tmp_poczatek->next; poprzedni = wyszukaj_poprzedni(poczatek, max); //wyciagnac mina poprzedni->next = max->next; // zalatac dziure max->next = poczatek; // wstawienie na poczatek poczatek = max; // zapisanie nowego poczatku }else{ tmp_poczatek = tmp_poczatek->next; } }else{ poprzedni = wyszukaj_poprzedni(poczatek, max); //wyciagnac mina poprzedni->next = max->next; // zalatac dziure max->next = poczatek; // wstawienie na poczatek poczatek = max; // zapisanie nowego poczatku } } // trick dla wstawienia ostatniego na poczatek poprzedni = wyszukaj_poprzedni(poczatek,tmp_poczatek); poprzedni->next = NULL; tmp_poczatek->next = poczatek; poczatek = tmp_poczatek; return poczatek;
}@
Błąd w lini 38. Z każdym kolejnym wywołaniem ubawa pozycji w liście aż w końcu (podejrzewam) program odwołuje się do pola, które "nie istnieje". -
Zamień linie 37 i 38 albo całkiem skasuj linię 38.
-
Zwracanie wskaźników przez funkcję to śliska rzecz, naprawdę ;]
funkcja wyszukaj_poprzedni może zwrócić NULL a Ty sobie beztrosko linijkę dalej wywołujesz:
@poprzedni->next = max->next;@
albo
@poprzedni->next = NULL;@
Wypadaloby w każdym przypadku sprawdzić czy przypadkiem wskaźnik nie jest nullem, a ryzyko crasha się zminimalizuje.
-
Dlaczego zwracanie wskaźników przez funckję to śliska rzecz? Jak robić to inaczej?
Poprawiłem, teraz wygląda to tak:
@Lista * sortuj_rosnaco(Lista * akt_lst, int pole){
Lista * max, *poczatek, *tmp_poczatek, *poprzedni;
int ten_sam;
max = akt_lst;
poczatek = akt_lst;
tmp_poczatek = akt_lst;while(tmp_poczatek->next!=NULL){ max = tmp_poczatek; akt_lst = tmp_poczatek; ten_sam = 1; while (akt_lst!=NULL){ if (porownaj_elementy(max, akt_lst, pole)==1){ max = akt_lst; ten_sam--; } akt_lst = akt_lst->next; } if (ten_sam == 1){ if(tmp_poczatek != poczatek){ tmp_poczatek = tmp_poczatek->next; poprzedni = wyszukaj_poprzedni(poczatek, max); //wyciagnac max if (poprzedni!=NULL){ // tutaj zmiana nr 1 poprzedni->next = max->next; // zalatac dziure max->next = poczatek; // wstawienie na poczatek poczatek = max; // zapisanie nowego poczatku } }else{ tmp_poczatek = tmp_poczatek->next; } }else{ poprzedni = wyszukaj_poprzedni(poczatek, max); //wyciagnac max if (poprzedni!=NULL){ // tutaj zmiana nr 2 poprzedni->next = max->next; // zalatac dziure max->next = poczatek; // wstawienie na poczatek poczatek = max; // zapisanie nowego poczatku } } } // trick dla wstawienia ostatniego na poczatek poprzedni = wyszukaj_poprzedni(poczatek,tmp_poczatek); if (poprzedni!=NULL) {// tutaj zmiana nr 1 poprzedni->next = NULL; tmp_poczatek->next = poczatek; poczatek = tmp_poczatek; } return poczatek;
}@
Niestety program dalej nie działa poprawnie... -
bq. Dlaczego zwracanie wskaźników przez funckję to śliska rzecz? Jak robić to inaczej?
Właśnie dlatego że wszędzie trzeba później sprawdzać poprawność wskaźnika, inaczej to można zwracać referencję do obiektu.
Odapliłem Twój program i jest crash hell, cokolwiek nie kliknę to wywala się apka ;]
Chociażby w tym miejcu:
@ if (el1->nazwa >= el2->nazwa) @Dobrze by było jakbyś zapoznał się ze standardowymi kontenerami np. "QList":http://qt-project.org/doc/qt-4.8/qlist.html, bo Twoja implementacja listy to po pierwsze odkrywanie koła na nowo, a po drugie to zamota ze wskaźnikami powoduje totalny brak stabilności.
-
bq. Odapliłem Twój program i jest crash hell, cokolwiek nie kliknę to wywala się apka ;]
Jeśli nie poda się prawidłowej ścieżki do zdjęć to program się wywala od razu. A dane poprawnie odczytywać będzie chyba tylko z 40D.
bq. Dobrze by było jakbyś zapoznał się ze standardowymi kontenerami np. QList [qt-project.org], bo Twoja implementacja listy to po pierwsze odkrywanie koła na nowo, a po drugie to zamota ze wskaźnikami powoduje totalny brak stabilności.
Chyba wchodzimy na zbyt wysoki poziom. :) Nie ma już czasu na przebudowanie całego projektu, może uda się znaleźć jeszcze błąd. W aplikacji konsolowej wszystko działało.
-
Udało mi się naprawić program. Błąd był zupełnie gdzie indziej, wystarczyło dopisać jedną linijkę.
Mam jeszcze pytanie. Czy da się zrobić i czy dużo jest roboty z tym, aby użytkownik wybierał lokalizację z wykorzystaniem czegoś w rodzaju menadżera windowsa? Tak jak podaję się lokalizację do zainstalowania w programach.
Dzięki za pomoc :)
-
Czy chodzi Ci o wybieranie ścieżki (folderu) lub pliku? Jeśli tak, to "QFileDialog":http://qt-project.org/doc/qt-5.0/qtwidgets/qfiledialog.html.
-
Powodzenia. Pamiętaj, aby w temacie dopisać w nazwie "[solved]" na znak rozwiązania problemu - rozumiem, że upływu danych już nie ma. :-)
-
Nie, nie ma. Miałem błąd w module mainwindow. Wywołując funkcję sortującą przekazywałem zawsze ten sam wskaźnik, tak więc stopniowo ginęły dane, które były wcześniej na liście dynamicznej. Dopisanie linijki załatwiło sprawę. Później usunąłem tą i kilka innych linijek, zmieniłem nazwę wskaźnika na inny (prawdłowy) i śmiga aż miło. Dziękuję raz jeszcze. :)