[C] Problem z malloc() [różnica windows/linux?]

0

Program finalnie ma być czytnikiem RSS, są jednak pewne problemy, które mam nadzieję, że pomożecie rozwiązać. O dziwo problematyczna funkcja działa jak należy na ubuntu, a na windowsie wyskakuje okienko z komunikatem, że program przestał odpowiadać.

Mam zdefiniowany typ ze strukturą (ktoś wie jak to się naprawdę nazywa? [bo chyba nie typ ze strukturą:p]):

typedef struct dane_kanalu {
    struct dane_kanalu *nast;
    char *tytul; /* na razie testuje tylko na tytule */
    long poczatek;
    long koniec;
} kanaly_rss;

Stworzyłem funkcję wyszukującą i zapisująca pozycje zaraz za znacznikiem otwierajacym [poczatek] i przed zamykajacym [koniec]. Nazwa znacznika jest podana w parametrze funkcji. np. <title>[POCZATEK]Tytuł wiadomości[KONIEC]</title> (mam nadzieję, że jest to zrozumiałe). Funkcja również w parametrze pobiera czy ma zapisać dane do struktury czy do tablicy dwuelementowej.

Na początku uruchamiam funkcję aby wyszukała początek i koniec każdej wiadomości (znacznik <item>...</item>), poźniej zapisuje te pozycje do struktury i tak powstaje lista struktur zawierających wypełnione jedynie pola poczatek i koniec (czyli pozycji od których zaczyna się dana wiadomości i kończy).
Do tej pory program działa bez żadnych zastrzeżeń.

Stworzyłem również drugą funkcję która ma za zadanie wyświetlić np. wszystkie tytuły wiadomości[co ma wyświetlić jest podane w parametrze].
Funkcja wygląda tak:

int zapisz_element( kanaly_rss *kanal, FILE *file, char *szuk ){
  kanaly_rss *wsk=kanal;
  int i=1;
  long save[2] = { 0 }; /* tylko 1 element ma byc 0 */
  long ile;
    while( wsk != NULL ){
        fseek(file, wsk->poczatek, 0);
        szukaj_krancow(wsk, file, szuk, save);
        ile = save[1] - save[0]+1; /* +1 bo np. "abc" -> 'c' znak 3., 'a' znak 1. -> 3-1=2 */
        fseek(file, save[0]-1, 0); /* -1 bo funkcje odczytuja nastepny znak */
        /* 1 */
        wsk->tytul = malloc((ile) * sizeof(char));
        if(fgets(wsk->tytul, ile+1, file) == NULL) return -1; /* +1 dla znaku '\0\' */
        printf ("Tytul %d: %s\n", i, wsk->tytul);
        wsk = wsk->nast;
        ++i;
        save[0] = 0;
    }
    return 0;
}

Co nie działa?
Program zawiesza się jedynie na windowsie(tylko na 7 testowałem) na Ubuntu działa bez problemów. Moment w którym przestaje odpowiadać chyba zależy od tego jaki plik (plik to oczywiście ściągnięty i zapisany kanał RSS [w tym prawie na 100% nie ma błędu bo robię to ręcznie]) przetwarza, ponieważ przy różnych plikach przy innej (czasami się oczywiście powtórzy) wiadomości się zawiesza, ale dla danego pliku __zawsze__przy tej samej wiadomości (zrozumiałe? chyba tak).
Nie wiem co jest dokładnie źle, ale zawsze zawiesza się program przy którymś tam przypisaniu adresu zarezerwowanej pamięci do wsk->tytul (linia po /* 1 */). Na 100% program się nie zawiesza z powodu braku pamięci [chyba oczywiste bo malloc inaczej wtedy reaguje :p, poza tym niektóre pliki posiadające 60+ wiadomości odczytuje bez problemu a niektóre przy 10 wiadomości się zawiesza)

Co już próbowałem robić?
Próbowałem w miejsce /* 1 */ wstawiać różne rzeczy:
Przy wstawieniu:

printf("dziala?");
malloc(1);
printf("zarezerwowalo");

REZULTAT: Program wyświetla tyle razy "dziala?zarezerwowalo" ile razy poprawnie wyświetla wiadomość. Przy wiadomości przy której się zawiesza widoczny jest jedynie napis "dziala?"

Przypominam, że błąd występuje jedynie na moim laptopie z windows 7 przy Ubuntu nie było tego problemu.

Program kompiluje się bez błędów i ostrzeżeń z flagą -Wall.

1

ile = save[1] - save[0]+1+1; zapomniałeś zaalokować miejsce na \0. Z kolei w fgetsie te +1 wywal. Nie jestem pewien czy to jest wszystko, ale tyle na pierwszy rzut oka zaobczyłem.

1

Na początek podpadło mi to:

long save[2] = { 0 }; /* tylko 1 element ma byc 0 */

Niestety to tak nie działa. Przy takiej inicjalizacji wszystkie elementy będą równe 0. Sprawdź sam: http://ideone.com/SVn9T Nie do końca jestem pewien, czy to cokolwiek zmienia w Twoim kodzie.

0

Wpadając w skrajność pomyślałem, że zmarnuje pamięć już na starcie i zobaczę, czy dalej będzie tak samo.

Do funkcji main dopisałem zaraz po deklaracji zmiennych pętlę marnującą pamięć:

for(i=0; i <1000; ++i) malloc(50);

reszty programu nie edytując.
Po uruchomieniu programu wyświetliło wszystkie wiadomości z kanału który bez tego nie działał. Oczywiście jest to absurd, aby takie coś zostawić "bo program wtedy działa", ale dla zaawansowanych programistów może będzie to wartościowa wskazówka.

Za chwilę jeszcze sprawdzę czy też działa na innych plikach

0
Endrju napisał(a):

Na początek podpadło mi to:

long save[2] = { 0 }; /* tylko 1 element ma byc 0 */

Niestety to tak nie działa. Przy takiej inicjalizacji wszystkie elementy będą równe 0. Sprawdź sam: http://ideone.com/SVn9T Nie do końca jestem pewien, czy to cokolwiek zmienia w Twoim kodzie.

Dzięki za uwagę, nic zupełnie to nie zmienia bo sprawdzana jest wartość jedynie pierwszego elementu w funkcji szukaj_krancow(). Drugi jest po to, aby przechowywał zwróconą wartość przez funkcję.

0

wklej większy fragment kodu. możlwe, że nie zaalokowałeś pamięci na kolejne elementy listy.

0

Zastanów się nad tym, co napisał @krwq w pierwszym poście. Co ta funkcja zwraca przez argument save? To ma być jakaś długość? Dlaczego znaki indeksujesz od 1? (Komentarz sugeruje coś takiego) Pamiętaj, że jeżeli napis ma mieć N znaków to musisz zaalokować N+1 znaków na dodatkowy znak \0. Zaalokowałeś ile a każesz wczytać ile+1 (łącznie z \0) . To oznacza, że fgets wczyta ile znaków a znak \0 postawi poza zaalokowanym obszarem.

0

funkcja do zapisywania pozycji:

void zapisz_pozycje(kanaly_rss *kanal, long poczatek, long koniec){
  kanaly_rss *wew_kop, *nowy;
    wew_kop = kanal;
    while (wew_kop->nast != NULL) wew_kop = wew_kop->nast;
    nowy = malloc (sizeof(kanaly_rss));
    nowy->poczatek = poczatek;
    nowy->koniec = koniec;
    nowy->nast = NULL;
    if(wew_kop->poczatek == -1){ /* 1*/
        wew_kop->poczatek = nowy->poczatek;
        wew_kop->koniec = nowy->koniec;
    }else
        wew_kop->nast = nowy;
}

W / 1 / sprawdza czy jest to pierwszy element, bo na początku programu zapisuje w kanal->poczatek = -1 [w celu sprawdzenia czy została już tam zapisana jakaś wartość czy nie]. Na pewno są lepsze/szybsze sposoby i chętnie bym je poznał [z moich pomysłów to wydawało się najlepszym rozwiązaniem] :)

0
Endrju napisał(a):

Zastanów się nad tym, co napisał @krwq w pierwszym poście. Co ta funkcja zwraca przez argument save? To ma być jakaś długość? Dlaczego znaki indeksujesz od 1? (Komentarz sugeruje coś takiego) Pamiętaj, że jeżeli napis ma mieć N znaków to musisz zaalokować N+1 znaków na dodatkowy znak \0. Zaalokowałeś ile a każesz wczytać ile+1 (łącznie z \0) . To oznacza, że fgets wczyta ile znaków a znak \0 postawi poza zaalokowanym obszarem.

Jeżeli save[0]==0 to zapisuje w save[0]=poczatek a save[1]=koniec (sa to pozycje w pliku zwrócone przez ftell), jeżeli save[0]==1 to zapisuje dane do struktury i save[1] jest bezużyteczne. inaczej tego nie potrafiłem wymyślić (jak ktoś ma lepszy pomysł to chętnie o nim usłyszę).

0

znajdź dokładnie miejsce w którym się zwiesza (zakładam, że dostajesz access violation?) - użyj do tego celu debuggera albo printfów
wklej całą funkcję, w której się to dzieje i zaznacz tą linię jakimś komentarzem.

0
krwq napisał(a):

znajdź dokładnie miejsce w którym się zwiesza (zakładam, że dostajesz access violation?) - użyj do tego celu debuggera albo printfów
wklej całą funkcję, w której się to dzieje i zaznacz tą linię jakimś komentarzem.

W poście otwierającym temat sprawdziłem to i opisałem pod tytułem 'Co już próbowałem robić?'. Użyłem tam printfów i pokazało, że program zawiesza się przy funkcji malloc().

0

do mnie trochę nurtuje co ten fseek robi, po prostu nie wiem co oznacza te 0 w ostatnim parametrze, czy to SEEK_SET, czy SEEK_CUR czy może SEEK_END. nie specjalnie chce mi się to sprawdzać, ale przemyślałbym osobiście tą linię chwilę dłużej

0
krwq napisał(a):

do mnie trochę nurtuje co ten fseek robi, po prostu nie wiem co oznacza te 0 w ostatnim parametrze, czy to SEEK_SET, czy SEEK_CUR czy może SEEK_END. nie specjalnie chce mi się to sprawdzać, ale przemyślałbym osobiście tą linię chwilę dłużej

fseek(FILE *file, long ile, 0/1/2);
3 parametr informuje od którego miejsca ma być przesunięcie dla 0 od początku pliku dla 1 od aktualnej pozycji dla 2 od końca pliku (wtedy zmienna ile chyba musi być ujemna? [nie jestem pewny])

Czyli w moim przypadku przesunięcie jest zawsze od początku pliku do miejsca "poczatek". To na pewno działa bo przecież wyświetla dobrze kilka/naście pierwszych wiadomości zależnie od pliku.

0

Mi się z kolei ta linia nie podoba

ile = save[1] - save[0]+1; /* +1 bo np. "abc" -> 'c' znak 3., 'a' znak 1. -> 3-1=2 */

Na "abc" potrzebujesz 4 znaki, bo jeszcze '\0'. Printf stosowany samotnie nie jest dobrą informacją przy debugowaniu padających programów, po nim wywołaj flush, ponieważ jest to wyjście buforowane i wszystko nie musi wyjść na konsolę w przypadku padnięcia. Dodatkowo gdy ile=0 to tytul może mieć przez malloca przypisane 0 (NULL), jest to implementation defined.

Edit: Zauważyłem, że już ktoś Ci o tym mówił. Jeszcze bym się upewnił, czy stdlib.h jest dołączony, choć to powinno wyjść przy -Wall.

0
Zjarek napisał(a):

Mi się z kolei ta linia nie podoba

ile = save[1] - save[0]+1; /* +1 bo np. "abc" -> 'c' znak 3., 'a' znak 1. -> 3-1=2 */

Na "abc" potrzebujesz 4 znaki, bo jeszcze '\0'. Printf stosowany samotnie nie jest dobrą informacją przy debugowaniu padających programów, po nim wywołaj flush, ponieważ jest to wyjście buforowane i wszystko nie musi wyjść na konsolę w przypadku padnięcia. Dodatkowo gdy ile=0 to tytul może mieć przez malloca przypisane 0 (NULL), jest to implementation defined.

Edit: Zauważyłem, że już ktoś Ci o tym mówił. Jeszcze bym się upewnił, czy stdlib.h jest dołączony, choć to powinno wyjść przy -Wall.

Również w miejsce /* 1 */ (w pierwszym poście) wstawiałem również printf("%ld", ile); i wychodziła poprawna wartość (ilość znaków w tytule). Również przypominam, że program zawiesza się po wpisaniu malloc(1); co jest już bezwzględnie różne od zera.

0

tak, ale zwróc uwagę, że nawet na pustego stringa potrzebujesz w pamięci 1 znak, a nie 0.

0
krwq napisał(a):

tak, ale zwróc uwagę, że nawet na pustego stringa potrzebujesz w pamięci 1 znak, a nie 0.

Natychmiast gdy napisałeś o tym błędzie zmieniłem to w programie, wiem dlaczego tak jest i w pełni się z tobą zgadzam.

Nie rozumiem natomiast dlaczego funkcja nie działa jak przed jej uruchomieniem nie zmarnuje pamięci malloc(5000);
Jak by było odwrotnie można by wtedy podejrzewać, że brakuje pamięci i program się zawiesza (chociaż malloc powiniem zwrócić NULL jak nie udało się przypisać pamięci a nie zawiesić program), ale jest tak, że funkcja działa jak jest mniej pamięci tego już zupełnie nie potrafię pojąć?

Na Ubuntu wszystko działa OK (czyli bez malloc(5000) i bez zawieszania programu).

0

prawie na pewno próbujesz operować na pamięci która nie jest dla Ciebie dostępna. tymi mallocami prawdopodobnie przypadkowo dajesz sobie do tej pamięci

0

Usunąłem nie potrzebne rzeczy służące do sprawdzania działania (typu printfy przed i po mallocu, malloc(5000)), pozostawiając kod takim jaki powinien być. Okazało się, że już wszystko działa.

Dzięki wielkie za pomoc.

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