• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Problem z funkcją wczytującą dane z pliku do zalokowanej przeze mnie tablicy dwuwymiarowej

Object Storage Arubacloud
0 głosów
168 wizyt
pytanie zadane 13 maja 2020 w C i C++ przez Hubertius Bywalec (2,970 p.)
edycja 13 maja 2020 przez Hubertius

Hej :)

Muszę rozpisać poniższą funkcję z pewnego zadania:

int load_words(char ***words, const char *filename);
Funkcja wczytuje listę słów z pliku words do tablicy words. 
Tablicę wskaźników na słowa *words należy zakończyć wartością NULL.

Wyrazy w pliku zapisane są w następujący sposób (dla każdego słowa):

4 bajty - długość n wyrazu (bez terminatora),
n bajtów - kolejne znaki słowa (bez terminatora).
Funkcja może zaalokować na tablicę wynikowę words jedynie tyle pamięci ile będzie potrzebne do przechowywania listy wyrazów.

Wartość zwracana: 0 - w przypadku sukcesu.
1 - w przypadku przekazania błędnych danych, 
2 - w przypadku, gdy plik nie istnieje,
3 - w przypadku stwierdzenia uszkodzonej zawartości pliku, 
4 - w przypadku niepowodzenia alokacji pamięci,

W przypadku wystąpienia błędu funkcja powinna ustawić *words wartością NULL.

Na tą chwilę tak mam to rozpisane:

nt load_words(char ***words, const char *filename)
{
    if( words == NULL || filename == NULL )
    {
        return 1;
    }
    FILE *f = fopen(filename,"r");
    if( f == NULL )
    {
        return 2;
    }
    int c = 'a';
    int i = 0;
    int rows = 0;
    while( c != EOF )
    {
        c = fgetc(f);
        if( c == '\n' )
        {
            rows++;
        }
        i++;
    }
    fseek(f,0,SEEK_SET);
    rows++;
    if( i == 0 )
    {
        fclose(f);
        return 3;
    }
    *words = (char **) malloc(sizeof(char *) * (rows + 1));
    if( *words == NULL )
    {
        fclose(f);
        return 4;
    }
    int j = 0;
    int licznik = 0;
    while( c != EOF && licznik < rows  )
    {
        c = fgetc(f);
        j++;
        if( c == '\n')
        {
            *( (*words) + licznik ) = (char *)malloc(sizeof(char) * j);
            if(  *( (*words) + licznik )  == NULL )
            {
                int get_out;
                for(get_out = 0; get_out < licznik; get_out++)
                {
                    free(*( (*words) + get_out ));
                }
                fclose(f);
                free(*words);
                return 4;
            }
            licznik++;
            j = 0;
        }
    }
    *((*words) + rows) = NULL;
    fseek(f,0,SEEK_SET);
    j = 0;
    licznik = 0;
    while( c != EOF && i < rows  )
    {
        c = fgetc(f);
        if( c != EOF )
        {
            *(*( (*words) + i) + j) = c;
            if( c == '\n')
            {
                *(*( (*words) + i) + j) = '\0';
                i++;
                j = 0;
            }
            else
            {
                j++;
            }
        }
    }
    fclose(f);
    return 0;
}

Jednak na testach ciągle jestem informowany, że są jakieś błędy z alokacją. Między innymi:

iczba niezwolnionych bloków pamięci: 1 blok(ów)⏎
Sumaryczna wielkość wycieku pamięci: 16 bajt(ów)⏎

Wydawało mi się, że wszystko dobrze zabezpieczam, ale najwyraźniej czegoś brakuje. Ponadto mam wrażenie, że w mojej funkcji coś jednak niepoprawnie przeprowadzam alokację i czegoś brakuje. Tak więc proszę was, abyście wypowiedzieli się jakiego braku zabezpieczenia dotyczy powyższy wyciek pamięci, a także czy na chwilę obecną mój tok myślenia jest prawidłowy. Z góry dziękuję za wszelkie odpowiedzi.  :)

komentarz 13 maja 2020 przez j23 Mędrzec (194,920 p.)
Ta twoja funkcja nie powinna czytać zawartość binarnie, bo z opisu wynika, że plik ze słowami nie jest typowym plikiem tekstowym?
komentarz 13 maja 2020 przez Hubertius Bywalec (2,970 p.)
Tak, ma czytać zawartość z pliku o rozszerzeniu .bin, a nie .txt
komentarz 13 maja 2020 przez j23 Mędrzec (194,920 p.)

Zatem ten kod jest źle napisany. Powinieneś do czytania z pliku użyć funkcji fread. Mniej więcej tak to powinno wyglądać:

uint32_t n;

while (fread(&n, 4, 1, f) == 1) {
    char* word = malloc(n + 1);
    if (fread(word, 1, n, f) == n) {
        word[n] = 0;
        // tu dodajesz słowo 'word' do tablicy
    }
}

No i oczywiście strumień plikowy powinieneś otworzyć w trybie binarnym.

komentarz 13 maja 2020 przez Hubertius Bywalec (2,970 p.)

Okej, czyli jak rozumiem w instrukcji pętli sprawdzasz, czy poprawnie przyjmuje te 4 bajty (długość słowa). Następna linijka to jak rozumiem alokacja na... obecne słowo? Słowa?

Nie do końca rozumiem tego:

if (fread(word, 1, n, f) == n) {
        word[n] = 0;

Czy to zczytywanie tych wszystkich słów z word? I dlaczego porównujesz to z n?

Kurde, zupełnie tego nie rozumiem już chyba teraz.   :/

komentarz 13 maja 2020 przez j23 Mędrzec (194,920 p.)

Następna linijka to jak rozumiem alokacja na... obecne słowo? Słowa?

Słowo.

Czy to zczytywanie tych wszystkich słów z word?

Jak z word? Czytanie z pliku do word.

I dlaczego porównujesz to z n?

Sprawdzam poprawność przeczytania n elementów jednobajtowych. Dzięki temu wiem, że wszytko się poprawnie przeczytało.

zupełnie tego nie rozumiem

Przecież to proste. Pętla czyta kolejne słowa z pliku. Najpierw czyta długość słowa, później przydziela odpowiednią ilość pamięci dla niego, po czym czyta słowo. Jedyne co tam musisz zrobić, to dodawanie słów we wskazanym przeze mnie miejscu do tablicy słów. Z funkcją realloc to zaledwie dwie linijki kodu ;)

1 odpowiedź

0 głosów
odpowiedź 13 maja 2020 przez mokrowski Mędrzec (155,460 p.)
edycja 13 maja 2020 przez mokrowski
W 31 linii alokujesz words. Gdzie go zwalniasz dla przypadku dojścia do 77 linii?

PS. Twoja funkcja ma zbyt wiele odpowiedzialności. Na początku np. zliczasz ilość linii. Wydziel to zadanie i inne do oddzielnych funkcji.
komentarz 13 maja 2020 przez Hubertius Bywalec (2,970 p.)
edycja 13 maja 2020 przez Hubertius

Hmm... wydawało mi się, że tym się zajmuje w linii 54 (w przypadku błędu dla alokacji pamięci dla kolumn dla poszczególnych wierszy) i w linii 32. Możesz rozwinąć czemu miałbym zaraz po ostatnim while-u przed końcem funkcji zwalniać pamięć? To w zadaniu nie byłoby zbyt dobre, ponieważ potem dalej w zadaniu muszę wykorzystać tą tablicę.

P.S To ostatecznie zdanie to kompletne masło maślane, ale mam nadzieję, że znaczenie tego co powiedziałem jest zrozumiałe.  :)

P.S 2 Poprawiłem błąd w linii 32. Tam w instrukcji warunkowej powinno być:

*words == NULL

 

komentarz 13 maja 2020 przez mokrowski Mędrzec (155,460 p.)
Podziel na funkcje a sam zobaczysz.
komentarz 13 maja 2020 przez j23 Mędrzec (194,920 p.)

IMO to zliczanie linii jest zbędne. Zwykły realloc wystarczy, a i kod się uprości.

Podobne pytania

0 głosów
0 odpowiedzi 92 wizyt
0 głosów
1 odpowiedź 1,555 wizyt
pytanie zadane 4 lipca 2017 w C i C++ przez kyly Początkujący (260 p.)

92,552 zapytań

141,399 odpowiedzi

319,533 komentarzy

61,938 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...