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

Zapisanie danych ze struktury do pliku

0 głosów
127 wizyt
pytanie zadane 27 kwietnia w C i C++ przez miodowy Nowicjusz (120 p.)

Cześć,

nie wiem dlaczego nie działa mi ten kod. Chcę zapisać dane ze struktury do pliku. Czy fwrite muszę w pętli umieścić? Czy dobrze malloc użyty?

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

struct data{
int id;
char name[50];
char lastName[50];
};
int main()
{
    int n,i;
   FILE *fp;
   fp = fopen("plik.txt", "w");
   if (fp == NULL)
    {
        printf("\nBłąd otwarcia\n");
        exit (1);
    }

   printf("podaj liczbe osob: ");
   scanf("%d", &n);
   struct data *wsk;
   wsk=malloc(n*sizeof(struct data));
   for(i=0; i<n; i++)
   {
       printf("Podaj id, imie, nazwisko: ");
       scanf("%d %s %s", &(wsk+i)->id, &(wsk+i)->name, &(wsk+i)->lastName);

   }

   for(i=0; i<n; i++)
   {
       printf("Id, imie, nazwisko: %d %s %s\n", (wsk+i)->id, (wsk+i)->name, (wsk+i)->lastName);


   }
fwrite(wsk, sizeof(struct data), 1, fp);
   if(fwrite != 0)
        printf("Zapisano !\n");
    else
        printf("Błąd !\n");

    fclose (fp);

   return 0;
}

 

3 odpowiedzi

0 głosów
odpowiedź 27 kwietnia przez Arkadiusz Sikorski Pasjonat (19,320 p.)

nie wiem dlaczego nie działa mi ten kod

A mógłbyś powiedzieć, CO Ci konkretnie nie działa? 

Z tego, co sam zauważyłem, to:

1)

wsk=malloc(n*sizeof(struct data));

zmień na

wsk=(struct data*)malloc(n*sizeof(struct data));

(potrzebne rzutowanie)

2)

fwrite(wsk, sizeof(struct data), 1, fp);
if(fwrite != 0)

zmień na

int status = fwrite(wsk, sizeof(struct data), 1, fp);
if(status != 0)

bo w tym, co napisałeś, próbujesz porównać wskaźnik na funkcję fwrite z zerem, co jest w tym kontekście bez sensu. Chcesz sprawdzić wartość zwracaną przez fwrite z zerem.

3) Dodaj też zwalnianie danych przy użyciu free(), które zaalokowałeś używając malloc().

komentarz 27 kwietnia przez miodowy Nowicjusz (120 p.)
Zapisuje do pliku, ale gdy otwieram plik to dziwne znaki tam są tj. ścieżka dysku C czy jak to nazwać
komentarz 27 kwietnia przez Arkadiusz Sikorski Pasjonat (19,320 p.)

W zaalokowanej pamięci możesz mieć śmieci, zapisujesz całe 50 bajtów dla name i 50 dla lastname. Jeśli wczytujesz od użytkownika, dajmy na to, 5 bajtów dla zmiennej name, to w pamięci wygląda to tak:

[A][B][C][D][E][\0][   -  pozostałe 44 bajty, w których mogą być śmieci  -   ]

Wtedy podczas zapisu do pliku lądują losowe rzeczy z pamięci ("pozostałe 44 bajty"), dlatego w pliku dostajesz jakieś dziwne rzeczy.

Wyczyść pamięć przed pobraniem danych (np. wypełnij zerami) należącą do struktury (albo przynajmniej do pól name i lastname), na przykład używając memset

komentarz 27 kwietnia przez DeBos123 Nałogowiec (35,670 p.)

@Arkadiusz Sikorski,

W języku C przy używaniu funkcji malloc rzutowanie nie jest potrzebne: https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc

0 głosów
odpowiedź 27 kwietnia przez mokrowski VIP (110,820 p.)

W *alloc(...) nie jest potrzebne rzutowanie w języku C. *alloc(...) zwraca typ void * który w standardzie konwertuje się automatycznie do typu docelowego. Z kolei w C++ konsekwencje użycia *alloc(...) są tak drastyczne że bywa wręcz w kodzie C++ zabroniony. Ale tylko w C++, reguły języka nakazują rzutować.

memset(...) także jest zbędny. Wystarczy (od C90) przypisanie {0} w trakcie inicjalizacji do tablicy lub nieco asekuranckie calloc(...) dla wcześniejszych standardów.

W tym kodzie brakuje:

1. Alokacji tablicy na podaną ilość osób.

2. Poprawnego obsłużenia błędów.

3. Trochę niezręczne jest obsługiwanie indeksów poprzez adresy ale zapewne "kazali".

4. Wycieki pamięci...

5. Z racji tego że nie ma wydzielonych funkcji (a IMHO powinny być), prawidłowa obsługa błędów alokacji będzie wymagała idiomu z goto który bywa źródłem zażartych dyskusji aczkolwiek jest szeroko używany.

6. No i nazewnictwo. O ile i czy j to "zwyczajowe nazwy", to nazywanie danych osoby data, to pomyłka.

7. scanf(...) także bym nie używał bo to funkcja niebezpieczna ale tu już nie zmieniałem (w konsekwencji program ma potencjalną podatność :-/ )

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

struct person {
    int id;
    char name[50];
    char lastName[50];
};

extern int errno;

int main(void)
{
    size_t persons_counter;
    struct person * persons_table;
    size_t count_written;
    size_t i;
    FILE *fp;

    fp = fopen("plik.txt", "w");
    if (fp == NULL)
    {
        fprintf(stderr, "Błąd operacji na pliku plik.txt: %s.\n", strerror(errno));
        goto exit_fail;
    }

    printf("Podaj liczbe osob: ");
    scanf("%zd", &persons_counter);
    persons_table = calloc(sizeof(struct person), persons_counter);

    if (persons_table == NULL) {
        fprintf(stderr, "Błąd alokacji tablicy: %s.\n", strerror(errno));
        goto exit_1;
    }

    for(i = 0; i < persons_counter; ++i)
    {
        printf("Podaj id, imie, nazwisko: ");
        scanf("%d %s %s",
            &(persons_table + i)->id,
            (char *)&(persons_table + i)->name,
            (char *)&(persons_table + i)->lastName);
    }

    for(i = 0; i < persons_counter; ++i)
    {
        printf("Id, imie, nazwisko: %d %s %s\n",
            (persons_table + i)->id,
            (persons_table + i)->name,
            (persons_table + i)->lastName);
    }

    count_written = fwrite(persons_table, sizeof(struct person), persons_counter, fp);
    if(count_written != 0) {
        printf("Zapisano !\n");
    } else {
        fprintf(stderr, "Błąd zapisu: %s\n", strerror(errno));
    }

    free(persons_table);
    fclose (fp);
    return EXIT_SUCCESS;

exit_1:
    fclose(fp);
exit_fail:
    return EXIT_FAILURE;
}

 

komentarz 27 kwietnia przez DeBos123 Nałogowiec (35,670 p.)

nazywanie danych osoby data, to pomyłka.

Mógłbyś to bardziej rozwinąć?

Nazwy zmiennych ma w języku angielskim, co oczywiście na plus, a słowo data oznacza dane, więc nie rozumiem gdzie tu pomyłka.

komentarz 28 kwietnia przez mokrowski VIP (110,820 p.)

Proszę zerknij do kodu który podałem. person to struktura danych przechowująca dane osoby. Słowo "dane" nie oznacza nic co da się zrozumieć czytając kod. To tak jakbyś wszystkie dane w programach nazywał dane1, dane2, dane3 a funkcje funkcja1 funkcja2. Nazwy mają być znaczące bo kod piszesz dla ludzi. Jak pisałem i i j jest zwyczajowe. Nazwy na zmienne typu variable czy data to "tumiwisizm intelektualny".

komentarz 28 kwietnia przez DeBos123 Nałogowiec (35,670 p.)

Zerknąłem i jest bardziej czytelny, co nie zmienia faktu, że nazwanie tej zmiennej data w tym przypadku nie jest moim zdaniem pomyłką.

Patrząc na pola struktury ciężko się nie domyślić, że chodzi o dane osoby.

Jeżeli pola struktury byłyby nazwane np. in oraz ln ciężko byłoby się nie zgodzić, że nazwa data jest zbyt ogólna.

komentarz 28 kwietnia przez mokrowski VIP (110,820 p.)
Ok, życzę więc powodzenia na code review. EOT
komentarz 28 kwietnia przez miodowy Nowicjusz (120 p.)
1. co masz na myśli brakuje alokacji tablicy na podana liczbe osob, jak pobieram liczbę i za pomocą malloca rezerwuje pamięc dla struct data? ;)

2. Dlaczego użyłes calloca? mallociem nie mozna? :D
komentarz 28 kwietnia przez mokrowski VIP (110,820 p.)

Ad 1. Spoko, jest 23 linii, byłem nieprecyzyjny, brak zwolnienia zasobu.

Ad 2.

Dlaczego użyłes calloca? mallociem nie mozna? :D

Zapisuje do pliku, ale gdy otwieram plik to dziwne znaki tam są tj. ścieżka dysku C czy jak to nazwać

memset(...) także jest zbędny. Wystarczy (od C90) przypisanie {0} w trakcie inicjalizacji do tablicy lub nieco asekuranckie calloc(...) dla wcześniejszych standardów.

Podziel kod na funkcje bo źle się czyta. No i jasność co do standardu C była by istotna. W jednym miejscu deklarujesz zmienne przed użyciem (<C90) a w innym miejscu "w miejscu" (>C90).

Dla wprowadzania string'ów, reguły bezpiecznego kodowania zalecają fgets(...) zamiast scanf(...).

0 głosów
odpowiedź 27 kwietnia przez DeBos123 Nałogowiec (35,670 p.)
edycja 4 maja przez DeBos123
  • Linia 16: Błędy powinieneś wypisywać do stderr, czyli powinno być:
    fprintf(stderr,"\nBłąd otwarcia\n");
  • Linia 23: Powinieneś to zamienić na:
    wsk=malloc(n*sizeof(*wsk));

    jeżeli kiedyś zmienisz typ danych zmiennej wsk, a zapomnisz zmienić struct data to ciężko będzie ci znaleźć wyciek pamięci, który w ten sposób utworzysz.

  • Linia 27: Powinieneś się zabezpieczyć przez przepełnienem stosu itp. np. w taki sposób:

    scanf("%d %50s %50s", &(wsk+i)->id, &(wsk+i)->name, &(wsk+i)->lastName);
  • Dodana liczba 49 oznacza, że pierwsze 49 znaków z tego co wpisze użytkownik zostanie wpisane do zmiennej. Jeżeli zastanawiasz się czemu znajduje się tam liczba 49, a nie 50 to dlatego, że musi być jeszcze miejsce na \0 na końcu.

  • Linia 37: Powinieneś zamienić struct data na *wsk (patrz błąd z linii 23)

  • Linia 38: Porównujesz wskaźnik na funkcję fwrite z zerem co jest bez sensu w tym przypadku. Powinieneś to zamienić na:

    if(fwrite(wsk, sizeof(struct data), 1, fp)!=0){
  • Linia 38: Kolejnym problemem jest ta cyfra 1 przez którą zapisujesz dane tylko jednej osoby do pliku.

Podobne pytania

0 głosów
1 odpowiedź 40 wizyt
Porady nie od parady
Wynikowy wygląd pytania, odpowiedzi czy komentarza, różni się od tego zaprezentowanego w edytorze postów. Stosuj więc funkcję Podgląd posta znajdującą się pod edytorem, aby upewnić się, czy na pewno ostateczny rezultat ci odpowiada.Podgląd posta

66,451 zapytań

113,207 odpowiedzi

239,680 komentarzy

46,704 pasjonatów

Przeglądających: 259
Pasjonatów: 13 Gości: 246

Motyw:

Akcja Pajacyk

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

Oto dwie polecane książki warte uwagi. Pełną listę znajdziesz tutaj.

...