[C]Baza danych- dynamiczna alokacja

0

Witam. Pisze baze danych w C, ale mam problem z jedną funkcją. Pozamieniałem wszystkie swoje funkcje żeby dla danych wczytywanych z pliku dynamiczne alokował pamięć niestety gdy robię dynamiczną alokację dla funkcji usuwającej klasę, program nie dość że się zawiesza po usunięciu klasy to jeszcze kasuje całą zawartość pliku... Gdzie popełniam błąd...(dlugosc1 zmiennia obliczana w innej funkcji, jest to rozmiar pliku podzielony przez wielkość struktury). Dodam że w innych funkcjach(wyświetlanie, edycja, dodawanie) dynamiczna alokacja działa bez problemu. Z góry dzięki za pomoc.

void usun_klase(FILE *file, int dlugosc1) {
       struct klasa *klas;
       klas = (struct klasa *) malloc(dlugosc1 * sizeof(struct klasa));
       int count = 0, index = 0, indeks, licznik;
       FILE *tmp;

       if ((file = fopen("projekt1.dat", "r")) == NULL)
       {
               fputs("Nie moge otworzyc pliku!\n", stderr);
               exit(4);
       }
       if ((tmp = fopen("temp.dat", "w")) == NULL)
       {
               fputs("Nie moge otworzyc pliku!\n", stderr);
               exit(4);
       }

       while (count < MAXNM &&  fread(&klas[count].litera, sizeof(struct klasa),1, file) == 1)
       {
               count++;
       }
       puts("Oto lista klas:\n");
       while (index < count)
       {
               fread(&klas, sizeof(struct klasa), 1, file);
               printf("%d.Klasa: %c Profil: %s, Wychowawca: %s ", index +1,klas[index].litera,klas[index].profil, klas[index].wych);
               printf("\n");
               index++;
       }
       index = 0;
       printf("Podaj numer klasy,ktora chcesz usunac: ");
       scanf("%d", &indeks);
       indeks = indeks - 1;
       for (index = indeks; index < count; index++)
              klas[index]=klas[index+1];
       fwrite(klas, sizeof(struct klasa), index-1, tmp);
       printf("Klase usunieto!\n");
       fclose(file);
       fopen("projekt1.dat", "w");
       while (fread(&klas[count].litera, sizeof(struct klasa),1, tmp) == 1)
       {
               count++;
       }
       fwrite(klas, sizeof(struct klasa), index-1, file);
       fclose(file);
       fclose(tmp);
       free(klas);
       while (getchar() != '\n')
             continue;
}
0

Z tamtym problemem już sobie poradziłem, ale pojawił się nowy. Mianowicie: jak przekazać do pliku taką strukturę?

struct uczen{
       char *imie;
       char *nazwisko;
       int data;
       char klasa;
};

Dodawanie rekordu wygląda u mnie tak:

void dodaj_ucznia(FILE *file) {
       int count, index;
       char bufor[20];

       if ((file = fopen("file.dat", "a+b")) == NULL)
       {
               fputs("Nie moge otworzyc pliku!\n", stderr);
               exit(2);
       }
       puts("Ilu uczniow chcesz wprowadzic?\n");
       scanf("%d", &count);
       struct uczen *uczn;
       uczn = (struct uczen *) malloc(count * sizeof(struct uczen));
       for (index = 0; index < count; index++)
       {       
               while (getchar() != '\n')
                     continue;
               puts("Podaj do jakiej klasy nalezy uczen:");
               scanf("%c", &uczn[index].klasa);
               puts("Podaj imie: ");
               scanf("%s", bufor);
               uczn[index].imie = (char*) calloc((strlen(bufor)+1), sizeof(char));
               strcpy(uczn[index].imie, bufor);
               puts("Podaj nazwisko: ");
               scanf("%s", bufor);
               uczn[index].nazwisko = (char*) calloc((strlen(bufor)+1), sizeof(char));
               strcpy(uczn[index].nazwisko, bufor);
               puts("Podaj wiek: ");
               scanf("%d", &uczn[index].data);
               

       }
       fwrite(uczn, sizeof(struct uczen), count, file);
       fclose(file);
       for(index = 0; index < count; index++)
         free(uczn[index].imie);
       for(index = 0; index < count; index++)
         free(uczn[index].nazwisko);
       free(uczn);
}

a wyświetlanie tak:

void wyswietl_uczniow(FILE *file, int dlugosc) {
       struct uczen *uczn;
       uczn = (struct uczen *) malloc(dlugosc * sizeof(struct uczen));
       int count = 0, index = 0;
              if ((file = fopen("file.dat", "r")) == NULL)
       {
               fputs("Nie moge otworzyc pliku!\n", stderr);
               exit(4);
       }

       rewind(file);
       while (fread(&uczn[count].imie, sizeof(struct uczen),1, file) == 1)
       {
               count++;
       }

       puts("Oto lista uczniow:\n");
       while (index < count)
       {
               fread(&uczn, sizeof(struct uczen), 1, file);
                       
               printf("%d.Klasa: %c, Imie: %s, Nazwisko: %s, Wiek: %d ", index 1,uczn[index].klasa,uczn[index].imie,uczn[index].nazwisko, uczn[index].data);
               printf("\n");
               index++;
       }
       fclose(file);
       free(uczn);
}

Gdy dodam ucznia i wyświetlę uczniów podczas jednego uruchomienia programu to jako imie i nazwisko wypisze mi dziwne symbole, a gdy zamknę program, uruchomię ponownie i spróbuję wyświetlić uczniów to program się zawiesza... Próbowałem z tymi ampersandami przy fread i fwrite ale gdy usune ampersanda przy fread to program nie odczyta nic... Nie wiem, może z tymi flagami przy fopen coś jest nie tak, no ale próbowałem zmieniać na a, a+b,a+ i nic to nie dawało...

0

Bo jak zapisujesz taką strukturę, to zapisujesz do pliku tylko pola struktury, czyli m.in. wartości wskaźników (adresy), ale już nie zawartość pamięci na którą te wskaźniki wskazują. Tym bardziej, że bufory związane z imieniem i nazwiskiem mają w Twoim kodzie różną długość.

Rozwiązania widzę dwa... albo dodasz do struktury bufory o stałej długości (np. char imie[20]; char nazwisko[30]), albo bardziej rozbudujesz funkcję zapisującą (tj taka funkcja najpierw konwertuje daną strukturę do postaci tekstowej m.in. pobierając dane spod adresów wskazywanych przez pola imie i nazwisko, i dopiero do pliku zapisywalbys taki bufor tekstu).

Tak samo jeśli chcesz, aby kolejne struktury miały różną długość (zależnie od długości imienia i nazwiska) to gdzieś przy zapisywaniu musisz zapisać, która struktura ma jaką długość, abyś był następnie w stanie odczytać z pliku prawidłową ilość danych.

0

Murky--> Dzięki za odpowiedź.

albo dodasz do struktury bufory o stałej długości (np. char imie[20]; char nazwisko[30])
taka była poprzednia wersja programu, teraz muszę przerobić cały program na dynamiczną alokację...

bardziej rozbudujesz funkcję zapisującą
mógłbyś napisać jak taką funkcję rozbudować, bo nie za bardzo mam pomysł jak przekonwertować taką strukturę do postaci tekstowej...

0

No tworzysz sobie jakiś bufor tekstowy np. char bufor[666], a następnie zapisujesz do takiego bufora dane np. w postaci [<imie> + \n + <nazwisko> + \n + <data> + \n + <klasa>], i dopiero fwrite(bufor, sizeof(char), liczba_znakow, file) (zamiast \n możesz oczywiście stosować inne separatory).

I generalnie będziesz się musiał trochę nagimnastykować przy odczytywaniu, aby potem to posklejać i wepchać do struktur. Możesz np. odczytać zawartość całego pliku jako bufor tekstu, i dopiero taki tekst dzielić po separatorach lub dodatkowo zapisanych polach określających wielkości poszczególnych elementów/struktur.

Kombinować można różnie i ogólnie Twoim zadaniem jest odpowiednio zakodować wszystkie dane, które chcesz zapisać do pliku, a następnie odczytując je, odpowiednio je zdekodować według określonego algorytmu.

0

Heh, chyba sobie jednak daruję przerabianie tego programu. Nie mam zamiaru spędzić kolejnych godzin nad tym dziadostwem... Pojawił się inny problem. Gdy próbuję usunąć klasę za pomocą tej funkcji w DEVC++ to niekiedy bez problemu usuwa klasę, niekiedy usuwa wszystkie klasy, a niekiedy program się zawiesza. Gdy uruchamiam program przez putty na serwerze student.edu.agh.pl i wywołam usun_klase przeważnie usunie bez problemu ale niekiedy wyskakuje mi Segmenation fault. Ma ktoś jakis pomysł gdzie tkwi błąd?

void usun_klase(FILE *file, int dlugosc1) 
{
       struct klasa *klas;
       klas = (struct klasa *) malloc(dlugosc1 * sizeof(struct klasa));
       if (klas == 0) 
       {
		printf("Blad: Brak pamieci!\n");
		exit(1);
       }
       int count = 0, index = 0, indeks, licznik;
       FILE *tmp;

       if ((file = fopen("projekt1.dat", "r")) == NULL)
       {
               fputs("Nie moge otworzyc pliku!\n", stderr);
               exit(4);
       }
       if ((tmp = fopen("temp.dat", "w")) == NULL)
       {
               fputs("Nie moge otworzyc pliku!\n", stderr);
               exit(4);
       }

       while (fread(&klas[count].litera, sizeof(struct klasa),1, file) == 1)
       {
               count++;
       }
       puts("Oto lista klas:\n");
       while (index < count)
       {
               fread(&klas, sizeof(struct klasa), 1, file);
               printf("%d.Klasa: %c Profil: %s, Wychowawca: %s ", index +1,klas[index].litera,klas[index].profil, klas[index].wych);
               printf("\n");
               index++;
       }
       index = 0;
       printf("Podaj numer klasy,ktora chcesz usunac: ");
       scanf("%d", &indeks);
       indeks = indeks - 1;
       for (index = indeks; index < count; index++)
              klas[index]=klas[index+1];
       fwrite(klas, sizeof(struct klasa), index-1, tmp);
       printf("Klase usunieto!\n");
       fclose(file);
       fopen("projekt1.dat", "w");
       fread(&klas[count].litera, sizeof(struct klasa),index-1, tmp);
       fwrite(klas, sizeof(struct klasa), index-1, file);
       free(klas);
       fclose(file);
       fclose(tmp);
       while (getchar() != '\n')
             continue;
}
0

Proponuję zacząć debugowanie od zdeterminowania w jakich sytuacjach błąd się pojawia. Jak już ustalisz w jakich sytuacjach się pojawia to sprawdź, która część kodu jest za niego odpowiedzialna. I wtedy pomyśl dlaczego się pojawia. A czas spędzony na debugowaniu na pewno zaowocuje w przyszłości :)

0

Uruchomiłem gdb na serwerze agh i niekiedy wyskoczy mi:

Program received signal SIGSEGV, Segmentation fault.
0xfef2343b in realfree () from /usr/lib/libc.so.1

Nie wiem, naprawdę nie mam pojęcia jak znaleźć źródło tego błędu. Nie ma jakiegoś algorytmu według którego ten błąd wyskakuje, raz się pojawi, raz nie, raz przy funkcji usun_ucznia, a raz przy wyswietl_ucznia, nie mam pojęcia o co chodzi. To znaczy domyślam się, bo błąd zaczął się pojawiać gdy wprowadziłem dynamiczną alokację pamięci dla danych wczytywanych z pliku, ale jak to poprawić żeby działało?

0

Dla takich samych danych wejściowych, dla takiej samej kompilacji i na takim samym sprzęcie raz się błąd pojawia przy funkcji wyswietl_ucznia a raz przy funkcji usun_ucznia? Co masz na myśli, że błąd się pojawia przy jakiejś funkcji? W czym i jak to sprawdzasz?

0

Dla takich samych danych wejściowych, dla takiej samej kompilacji i na takim samym sprzęcie raz się błąd pojawia przy funkcji wyswietl_ucznia a raz przy funkcji usun_ucznia?

Już nie pamiętam czy to były te same dane, mogły być różne, ale błąd był ten sam.

Co masz na myśli, że błąd się pojawia przy jakiejś funkcji?

Mam na myśli to że uruchamiam jakąś funkcję z menu programu i albo od razu wyskoczy błąd, albo np po wybraniu którego ucznia usunąć.

W czym i jak to sprawdzasz?

Sprawdzam przez putty, a debugger to gdb na serwerze agh.
Teraz poprawiłem co nieco w programie, to znaczy wywaliłem alokację pamięci do osobnej funkcji i po prostu przekazuję tylko wskaźnik do tej pamięci wszystkim innym funkcją. W devc++ jeśli w pliku jest więcej niż jeden uczeń program wiesza się gdy wybiorę którego ucznia ma usunąć. Niekiedy po ponownym uruchomieniu uczeń faktycznie został usunięty, a niekiedy wyczyszczony zostaje cały plik file.dat. Co ciekawe gdy jest jeden uczeń w pliku, funkcja dział bez zarzutu, usuwa ucznia i wychodzi do menu.
Na serwerze agh póki co wszystko działa bez zarzutu, nie wywaliło mi jeszcze ani razu Segmentation Fault. Nie mam pojęcia o co chodzi...

1 użytkowników online, w tym zalogowanych: 0, gości: 1