[C] Drzewo binarne - wstawianie

Awatar użytkownika
Pneumokok
Użytkownik
Użytkownik
Posty: 48
Rejestracja: 2 sty 2011, o 14:29
Płeć: Mężczyzna
Lokalizacja: Małopolska
Podziękował: 11 razy
Pomógł: 1 raz

[C] Drzewo binarne - wstawianie

Post autor: Pneumokok »

Mam taki kod:

Kod: Zaznacz cały

#include <stdio.h>
#include <stdlib.h>

struct BIN
{
    int wartosc;
    struct BIN* lewo;
    struct BIN* prawo;
};

struct BIN* ustaw_korzen(struct BIN*, int);
void wstaw_element(struct BIN*, int);

int main()
{
    struct BIN* korzen=NULL;
    int liczba;
    puts("Podaj wartosc dla korzenia:"); scanf("%d", &liczba); fflush(stdin);
    korzen=ustaw_korzen(korzen, liczba);
    wstaw_element(korzen, 3);
    wstaw_element(korzen, 6);
    printf("%d %d %d", korzen->wartosc, korzen->lewo->wartosc, korzen->prawo->wartosc) ;
    
    korzen=NULL;
    getchar();
    return 0;
    
}

struct BIN* ustaw_korzen(struct BIN* korzen, int liczba)
{
    korzen=(struct BIN*) malloc (1*sizeof(*korzen) );
    korzen->wartosc=liczba;
    korzen->lewo=NULL;
    korzen->prawo=NULL;
    return korzen;
}

void wstaw_element(struct BIN* wezel, int liczba)
{
     if ( (wezel->wartosc) >= liczba)
     {
         if (wezel->lewo)
            wstaw_element(wezel->lewo, liczba);
         else
         {
             struct BIN* nowy=(struct BIN*) malloc (1*sizeof(*nowy) );
             wezel->prawo=NULL;
             wezel->lewo=nowy;
             nowy->wartosc=liczba;
             nowy->lewo=NULL;
             printf("
Na lewo: %d", wezel->lewo->wartosc);
             nowy->prawo=NULL;  nowy=NULL; wezel=NULL;
         }
     }
     else
     {
         if (wezel->prawo)
            wstaw_element(wezel->prawo, liczba);
         else
         {
             struct BIN* nowy=(struct BST*) malloc (1*sizeof(*nowy) );
             wezel->lewo=NULL;
             wezel->prawo=nowy;
             nowy->wartosc=liczba;
             nowy->lewo=NULL; 
             printf("
na prawo: %d", wezel->prawo->wartosc);
             nowy->prawo=NULL;  nowy=NULL; wezel=NULL;
         }
     }
     wezel=NULL;
}
    
Gdzieś tutaj zaszył się chyba problem z pamięcią... Najlepiej to widać gdy jako korzeń podacie 5 - wtedy na lewo będzie 3, na prawo 6, wyświetli się wewnątrz funkcji poprawnie, natomiast w kodzie głównym wyrzuci błąd...
Sam program jest niekompletny, nie ma odśmiecania pamięci itd... To tylko wersja przed-beta właściwie, sprawdzenie algorytmu
Czy ktoś mógłby na to spojrzeć? Z góry serdecznie dziękuję
Ostatnio zmieniony 21 cze 2011, o 11:19 przez Afish, łącznie zmieniany 1 raz.
Powód: Staraj się lepiej dobierać nazwy tematów, tak by wskazywały o czym jest treść zadania.
Afish
Moderator
Moderator
Posty: 2828
Rejestracja: 15 cze 2008, o 15:45
Płeć: Mężczyzna
Lokalizacja: Seattle, WA
Podziękował: 3 razy
Pomógł: 356 razy

[C] Drzewo binarne - wstawianie

Post autor: Afish »

Błąd powodują linijki:

Kod: Zaznacz cały

void wstaw_element(struct BIN* wezel, int liczba)
{
     if ( (wezel->wartosc) >= liczba)
     {
         if (wezel->lewo)
            wstaw_element(wezel->lewo, liczba);
         else
         {
             struct BIN* nowy=(struct BIN*) malloc (1*sizeof(*nowy) );
             wezel->prawo=NULL; <------- Ta tutaj
             wezel->lewo=nowy;
             nowy->wartosc=liczba;
             nowy->lewo=NULL;
             printf("
Na lewo: %d", wezel->lewo->wartosc);
             nowy->prawo=NULL;  nowy=NULL; wezel=NULL;
         }
     }
     else
     {
         if (wezel->prawo)
            wstaw_element(wezel->prawo, liczba);
         else
         {
             struct BIN* nowy=(struct BST*) malloc (1*sizeof(*nowy) );
             wezel->lewo=NULL; <-------- I ta również
             wezel->prawo=nowy;
             nowy->wartosc=liczba;
             nowy->lewo=NULL; 
             printf("
na prawo: %d", wezel->prawo->wartosc);
             nowy->prawo=NULL;  nowy=NULL; wezel=NULL;
         }
     }
     wezel=NULL;
}
   
Mam nadzieję, że rozumiesz, dlaczego. Jeżeli nie, to polecam zabawę z debuggerem, a w ostateczności zapytanie tutaj, to wytłumaczę.
Awatar użytkownika
Pneumokok
Użytkownik
Użytkownik
Posty: 48
Rejestracja: 2 sty 2011, o 14:29
Płeć: Mężczyzna
Lokalizacja: Małopolska
Podziękował: 11 razy
Pomógł: 1 raz

[C] Drzewo binarne - wstawianie

Post autor: Pneumokok »

Chyba rozumiem, wyzerowanie będącego tam już elementu (niepotrzebny NULL)... Faktycznie po skasowaniu działa. Dzięki za pomoc
Nie wziąłem pod uwagę tego, że przed dodaniem puste gałęzie korzenia i tak mają NULLa.

A to jeszcze jedno małe pytanko: czy zapis " wsk=NULL; " jest równoważy " wsk=0;" ?
Afish
Moderator
Moderator
Posty: 2828
Rejestracja: 15 cze 2008, o 15:45
Płeć: Mężczyzna
Lokalizacja: Seattle, WA
Podziękował: 3 razy
Pomógł: 356 razy

[C] Drzewo binarne - wstawianie

Post autor: Afish »

Według standardu tak - integer o wartości 0 będzie niejawnie zrzutowany na null-pointer odpowiedniego typu.
Awatar użytkownika
Pneumokok
Użytkownik
Użytkownik
Posty: 48
Rejestracja: 2 sty 2011, o 14:29
Płeć: Mężczyzna
Lokalizacja: Małopolska
Podziękował: 11 razy
Pomógł: 1 raz

[C] Drzewo binarne - wstawianie

Post autor: Pneumokok »

Czyli zaalokowanie tablicy wskaźników przez calloc jest równoważne z alokacją przez malloc i zapisaniu do każdego elementu NULLa?

Zadziała to także przy wskaźnikach typu char*, float* bądź double* ( pytam, ponieważ piszesz ,,integer o wartości ..." ) ?
Afish
Moderator
Moderator
Posty: 2828
Rejestracja: 15 cze 2008, o 15:45
Płeć: Mężczyzna
Lokalizacja: Seattle, WA
Podziękował: 3 razy
Pomógł: 356 razy

[C] Drzewo binarne - wstawianie

Post autor: Afish »

Nie, nie jest. Posiłkując się stroną a konkretniej zdaniem
"initializes all its bits to zero"
wnioskujemy, że nie wiemy nic więcej o zawartości zaalokowanej pamięci. Null-pointer nie jest zdefiniowany dokładnie w standardzie, określone są tylko cechy i jego zachowanie w odpowiednich sytuacjach jak dereferencja, porównanie z innym wskaźnikiem i tak dalej. To może być wskaźnik do adresu 0 (i tak to zazwyczaj jest), do dowolnego innego miejsca, jakiś wskaźnik dwubajtowy z dodatkową flagą etc. A to oznacza, że binarnie on nie musi składać się z samych zer.
Awatar użytkownika
Pneumokok
Użytkownik
Użytkownik
Posty: 48
Rejestracja: 2 sty 2011, o 14:29
Płeć: Mężczyzna
Lokalizacja: Małopolska
Podziękował: 11 razy
Pomógł: 1 raz

[C] Drzewo binarne - wstawianie

Post autor: Pneumokok »

Aha... Pojawił się nowy problem, być może ma to właśnie związek z tym, że wskaźnik na NULL nie musi być zerem... Otóż dołozyłem funkcję majacą zwalniać pamięć (" void zwolnij_pamiec(struct BIN* wezel) " ), i jak w kodzie - dodaję do drzewa 50 000 elementów. W momencie rozpoczecia programu zuzycie pamięci to 748 kb (domyślam się że chodzi o kilobajty, sprawdzałem przez menedźer zadań w XP), po wstawieniu zajmował 3496kb a po zwolnieniu 2416 kb (także obserwowałem w menedźerze).

Kod funkcji:

Kod: Zaznacz cały

void zwolnij_pamiec(struct BIN* wezel)
{
       if (wezel->prawo)
          zwolnij_pamiec(wezel->prawo);
       if (wezel->lewo)
          zwolnij_pamiec(wezel->lewo);
       wezel->prawo=NULL;
       wezel->lewo=NULL;
       //printf(" a ");
       free(wezel); wezel=NULL;
}
Pomyślałem to tak - schodzi w prawo i lewo, odkłada się rekurencyjnie na stosie - i wtedy od końca kasuje elementy (dopóki nie ma prawego potomka, dopóki nie ma lewego, te ląduja na stosie (Dopisane: chodzi mi o to, że dla każdego elementu będzie jedna rekurencja - czyli kazdy winien być usunięty)). Teoretycznie powinien przejść przez każdy węzęł/liść (dla małych ilości tak jest, zgadza mi sie ilość elementów które podam z ilością wyświetlonych " a " ), no ale patrząc na to że pamięć dla 50 000 elementów nie wraca do początkowych 748 kb nieco mnie dziwi... Może właśnie to że if (wsk) nie znaczy to samo co if (wsk!=NULL) powoduje ten problem? Choc z drugiej strony już chwilę używam tego skrótu i przenigdy nie było problemów...

Cały program:
Ukryta treść:    
getchar() po "wstawiłem" dodałem po to, by poczekał ze zwolnieniem aż zapsizę sobie ilość zajętej pamięci.

Kolejny raz proszę o pomoc, jak i serdecznie dziękuję za dotychczas udzieloną


@Edycja:
Dla 100 elementów zużycie pamięci po odśmieceniu jest większe niż przed nią, ot cudo
Afish
Moderator
Moderator
Posty: 2828
Rejestracja: 15 cze 2008, o 15:45
Płeć: Mężczyzna
Lokalizacja: Seattle, WA
Podziękował: 3 razy
Pomógł: 356 razy

[C] Drzewo binarne - wstawianie

Post autor: Afish »

Nie przejmowałbym się tym, co pokazuje menadżer zadań, a to ze względu na sposób zarządzania pamięcią przez system. Po zwolnieniu pamięć niekoniecznie musi być odliczona dla procesu, bo być może ten będzie chciał ją zaraz zająć ponownie, więc systemowi nie zależy na jak najszybszym wyczyszczeniu wszystkiego (o ile nie ma potrzeby). Jeżeli bardzo zależy Ci na sprawdzeniu, czy nie ma wycieków, to możesz użyć odpowiednich narzędzi jak valgrind lub purify. I nie, skrót if(wsk) nie powoduje błędu, bo najpopularniejsze kompilatory (czyli gcc, g++ i msvc) implementują null-pointer jako wskaźnik do adresu 0, więc wszystko jest okej.
ODPOWIEDZ