C - obsługa plików, błędy valgrinda

0

Cześć,

Mam do popełnienia taki oto program:

Zdefiniować typ strukturalny

typedef struct tosoba { char naz[20]; long id; }.

Opracować następujące funkcje obsługujące plik złożony ze zmiennych strukturalnych typu tosoba:

  1. Inicjalizującą zmienną typu tosoba losowymi wartościami;

Prototyp:

void Inicjuj(tosoba& r);
  1. Wyprowadzającą zawartość zmiennej typu tosoba na ekran;

  2. Dopisującą zmienną typu tosoba na końcu pliku o podanej nazwie;

Prototyp:

 void Fdopisz(FILE *f, char *nazwa, const tosoba *os);
  1. Odczytującą zmienną z zadanej pozycji w pliku o podanej nazwie i zapisującą daną do os;

Prototyp:

void Fczytaj (FILE *f, char *nazwa, long poz, tosoba *os);
  1. Wstawiającą, pomiędzy istniejące dane, nową daną na zadanej pozycji w pliku o podanej nazwie;

Prototyp:

void Fwstaw (FILE *f, char *nazwa, long poz, const tosoba *os);
  1. Usuwającą element z zadanej pozycji w pliku i zapisującą daną do os;

Prototyp:

void Fusun (FILE *f, char *nazwa, long poz, tosoba *os).

Przetestować opracowane funkcje w programie.

Napisałem wszystkie te funkcje oprócz Fusun gdyż zauważyłem, ze valgrind wywala błędy w funkcjach:

  1. Fdopisz
  2. Fwstaw

Tutaj jest mój kod:

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

struct tosoba7{
    char naz[20];
    long id;
};

void inicjalizuj(struct tosoba7 *os){
    os->id=rand()%1111;
    strcpy(os->naz,"nazw");
}

void wypisz7(struct tosoba7 *os){
    printf("%s \n%ld\n",os->naz,os->id);
}

void Fdopisz(FILE *f, char *nazwa, const struct tosoba7 *os){
    f=fopen(nazwa,"a");
    if(f){
        //Teoretycznie fseek jest niepotrzebne dla gdyż atrybut "a" automatycznie ustawia wskaźnik na koniec pliku.
        //fseek(f,0,2);
        fwrite(os,sizeof(struct tosoba7),1,f);
        fclose(f);
    }

}

void Fczytaj(FILE *f, char *nazwa, long poz, struct tosoba7 *os){
    f=fopen(nazwa,"r");
    if(f){
        fseek(f,sizeof(struct tosoba7)*poz,0);
        fread(os,sizeof(struct tosoba7),1,f);
        fclose(f);
    }
}

void Fwstaw(FILE *f, char *nazwa, long poz, struct tosoba7 *os){
    struct tosoba7 *tab=0;
    int iloscObiektow=0;
    f=fopen(nazwa,"r");
    if(f){
        //ustawienie wskaźnika na koniec pliku, żeby do obiektu rozmiar wczytać rozmiar pliku
        fseek(f,0,2);
        long rozmiar=ftell(f);
        iloscObiektow=rozmiar/sizeof(struct tosoba7);

        //przestawienie wskaźnika na początek pliki, żeby wczytać wszystkie pliki do tablicy dynamicznej
        fseek(f,0,0);
        tab=(struct tosoba7*)malloc(iloscObiektow*sizeof(struct tosoba7));
        fread(tab,sizeof(struct tosoba7),iloscObiektow,f);

        //realokowanie pamięci na komórkę o jeden większą, w której będzie dodawany nowy obiekt
        tab=(struct tosoba7*)realloc(tab,(iloscObiektow+1)*sizeof(struct tosoba7));

        //ustalenie pozycji dla nowego obiektu
        int i;
        for(i=iloscObiektow+1;i>=0;i--){
            if(i!=poz){
                tab[i]=tab[i-1];
            }
            else if(i==poz){
                tab[i]=*os;
                break;
            }
        }

        fclose(f);
    }

    f=fopen(nazwa,"w");
    if(f){
        fwrite(tab,sizeof(struct tosoba7),iloscObiektow+1,f);
        fclose(f);
    }
    if(tab)free(tab);
}

//void Fusun(FILE *f, char *nazwa, long poz, struct tosoba7 *os){

//}

int main(void){

    srand(time(NULL));

    FILE *init=fopen("zad7.txt","w");
    if(init){
        fclose(init);
    }

    struct tosoba7 s;
    inicjalizuj(&s);

    struct tosoba7 x;
    inicjalizuj(&x);

    printf("Dwa pierwsze obiekty:\n");
    wypisz7(&s);
    wypisz7(&x);

    FILE *plik=0;
    Fdopisz(plik,"zad7.txt",&s);
    Fdopisz(plik,"zad7.txt",&x);

    struct tosoba7 c;
    Fczytaj(plik,"zad7.txt",1,&c);
    printf("\nPlik odczytany z pozycji nr 1:\n");
    printf("%s\n%ld\n",c.naz,c.id);

    struct tosoba7 sx;
    inicjalizuj(&sx);
    printf("\nObiekt do pisania na pozycje nr 1:\n");
    wypisz7(&sx);

    Fwstaw(plik,"zad7.txt",1,&sx);

    printf("\nPo dopisaniu na poz nr 1\n");
    struct tosoba7 tab[3];
    int i;
    for(i=0;i<3;i++)
        Fczytaj(plik,"zad7.txt",i,&tab[i]);
    for(i=0;i<3;i++)
        wypisz7(&tab[i]);
    return 0;
}

Gdzie może być błąd? Nie mogę sobie z tym poradzić?

Pozdrawiam
Grzesiek

PS: U mnie typ nazywa się tosoba7 od numeru zadania.

0

A co dokładnie mówi valgrind?

1
f=fopen(nazwa,"ab");
f=fopen(nazwa,"wb");
f=fopen(nazwa,"rb");
0

Poprawiłem wg podpowiedzi @_13th_Dragon i dalej są błędy.
Oto one:

==4570== Memcheck, a memory error detector
==4570== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==4570== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==4570== Command: ./Testy
==4570== 
Dwa pierwsze obiekty:
nazw 
451
nazw 
492
==4570== Syscall param write(buf) points to uninitialised byte(s)
==4570==    at 0x4125CF3: __write_nocancel (syscall-template.S:81)
==4570==    by 0x40B77E0: _IO_file_write@@GLIBC_2.1 (fileops.c:1270)
==4570==    by 0x40B76C5: new_do_write (fileops.c:546)
==4570==    by 0x40B8D4D: _IO_do_write@@GLIBC_2.1 (fileops.c:519)
==4570==    by 0x40B862F: _IO_file_close_it@@GLIBC_2.1 (fileops.c:165)
==4570==    by 0x40ABD8F: fclose@@GLIBC_2.1 (iofclose.c:59)
==4570==    by 0x80489F9: Fdopisz (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570==    by 0x405F934: (below main) (libc-start.c:260)
==4570==  Address 0x4036005 is not stack'd, malloc'd or (recently) free'd
==4570== 

Plik odczytany z pozycji nr 1:
nazw
492

Obiekt do pisania na pozycje nr 1:
nazw 
655
==4570== Invalid write of size 4
==4570==    at 0x8048B8D: Fwstaw (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570==    by 0x8048786: main (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570==  Address 0x41fb8a0 is 0 bytes after a block of size 72 alloc'd
==4570==    at 0x402C63E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==4570==    by 0x8048B70: Fwstaw (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570==    by 0x8048786: main (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570== 
==4570== Invalid write of size 4
==4570==    at 0x8048B93: Fwstaw (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570==    by 0x8048786: main (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570==  Address 0x41fb8a4 is 4 bytes after a block of size 72 alloc'd
==4570==    at 0x402C63E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==4570==    by 0x8048B70: Fwstaw (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570==    by 0x8048786: main (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570== 
==4570== Invalid write of size 4
==4570==    at 0x8048B99: Fwstaw (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570==    by 0x8048786: main (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570==  Address 0x41fb8a8 is 8 bytes after a block of size 72 alloc'd
==4570==    at 0x402C63E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==4570==    by 0x8048B70: Fwstaw (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570==    by 0x8048786: main (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570== 
==4570== Invalid write of size 4
==4570==    at 0x8048B9F: Fwstaw (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570==    by 0x8048786: main (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570==  Address 0x41fb8ac is 12 bytes after a block of size 72 alloc'd
==4570==    at 0x402C63E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==4570==    by 0x8048B70: Fwstaw (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570==    by 0x8048786: main (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570== 
==4570== Invalid write of size 4
==4570==    at 0x8048BA5: Fwstaw (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570==    by 0x8048786: main (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570==  Address 0x41fb8b0 is 16 bytes after a block of size 72 alloc'd
==4570==    at 0x402C63E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==4570==    by 0x8048B70: Fwstaw (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570==    by 0x8048786: main (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570== 
==4570== Invalid write of size 4
==4570==    at 0x8048BAB: Fwstaw (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570==    by 0x8048786: main (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4570==  Address 0x41fb8b4 is not stack'd, malloc'd or (recently) free'd
==4570== 

Po dopisaniu na poz nr 1
nazw 
451
nazw 
655
nazw 
492
==4570== 
==4570== HEAP SUMMARY:
==4570==     in use at exit: 0 bytes in 0 blocks
==4570==   total heap usage: 11 allocs, 11 frees, 3,288 bytes allocated
==4570== 
==4570== All heap blocks were freed -- no leaks are possible
==4570== 
==4570== For counts of detected and suppressed errors, rerun with: -v
==4570== Use --track-origins=yes to see where uninitialised values come from
==4570== ERROR SUMMARY: 9 errors from 7 contexts (suppressed: 0 from 0)

Raz działa to dobrze, a raz nie działa i się wiesza, stąd pewnie te błędy.
Kurcze, jest zjedzone 'l' w wyrazie valgrind w tytule posta. Już nie mogę poprawić :-/

0

tutaj:

 
        //realokowanie pamięci na komórkę o jeden większą, w której będzie dodawany nowy obiekt
        tab=(struct tosoba7*)realloc(tab,(iloscObiektow+1)*sizeof(struct tosoba7));
 
        //ustalenie pozycji dla nowego obiektu
        int i;
        for(i=iloscObiektow+1;i>=0;i--){
            if(i!=poz){
                tab[i]=tab[i-1];
            }
            else if(i==poz){
                tab[i]=*os;
                break;
            }
        }

Wyłazisz poza tablicę. Skoro tablica ma iloscObiektow+1 elementów to nie możesz odnosić się do indeksu "iloscObiektow+1" bo takie indeksu nie ma. Najwyższy indeks to "iloscObiektow". Na koniec też wyłazisz poza tablicę bo jak i=0 to nie możesz zrobić tab[i-1]

0

Rzeczywiście masz rację, już to poprawiłem. Dalej występuje jednak taki błąd w funkcji Fdopisz:

 
==4718== Syscall param write(buf) points to uninitialised byte(s)
==4718==    at 0x4125CF3: __write_nocancel (syscall-template.S:81)
==4718==    by 0x40B77E0: _IO_file_write@@GLIBC_2.1 (fileops.c:1270)
==4718==    by 0x40B76C5: new_do_write (fileops.c:546)
==4718==    by 0x40B8D4D: _IO_do_write@@GLIBC_2.1 (fileops.c:519)
==4718==    by 0x40B862F: _IO_file_close_it@@GLIBC_2.1 (fileops.c:165)
==4718==    by 0x40ABD8F: fclose@@GLIBC_2.1 (iofclose.c:59)
==4718==    by 0x80489F9: Fdopisz (in /home/grzesiek/build-Testy-Desktop-Release/Testy)
==4718==    by 0x405F934: (below main) (libc-start.c:260)
==4718==  Address 0x4036005 is not stack'd, malloc'd or (recently) free'd
==4718== 

Coś jest nie tak z fwrite?

0

Przerób wstawianie i usuwanie następująco.
Wczytujesz jeden rekord - zapisujesz go do nowego pliku.
Potem następny i następny i następny ...
Gdzieś w środku pomijasz jeden z rekordów lub wstawiasz dodatkowy (w zależności od funkcji)
Po zakończeniu usuwasz stary plik i przemianowujesz nowy.

0

No ok, mogę tak zrobić ale podejrzewam u siebie w tej funkcji błędy z fwrite, czy fread. Mogę je też popełnić w Twoim sposobie dlatego chcę sobie debugować na razie swój algorytm.

Przecież w sumie funkcja Fdopisz jest bardzo prosta:

 void Fdopisz(FILE *f, char *nazwa, const struct tosoba7 *os){
    f=fopen(nazwa,"ab");
    if(f){
        //fseek(f,0,2);
        fwrite(os,sizeof(struct tosoba7),1,f);
        fclose(f);
    }

}

Zapisywanie rekordu następnego i nastepnego... będę realizował w podobny sposób, a tutaj właśnie jest błąd wykazywany przez valgrinda.

0

Po kiego przekazujesz parametr z którego nie korzystasz?

void Fdopisz(char *nazwa, const struct tosoba7 *os)
  {
   FILE *f;
   if(!(f=fopen(nazwa,"ab"))) return;
   fwrite(os,sizeof(struct tosoba7),1,f);
   fclose(f);
  }

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