• 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

VPS Starter Arubacloud
0 głosów
452 wizyt
pytanie zadane 27 kwietnia 2019 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 2019 przez Arkadiusz Sikorski Pasjonat (20,160 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 2019 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 2019 przez Arkadiusz Sikorski Pasjonat (20,160 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 2019 przez DeBos123 Nałogowiec (44,950 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 2019 przez mokrowski Mędrzec (155,460 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 2019 przez DeBos123 Nałogowiec (44,950 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 2019 przez mokrowski Mędrzec (155,460 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 2019 przez DeBos123 Nałogowiec (44,950 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 2019 przez mokrowski Mędrzec (155,460 p.)
Ok, życzę więc powodzenia na code review. EOT
komentarz 28 kwietnia 2019 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 2019 przez mokrowski Mędrzec (155,460 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 2019 przez DeBos123 Nałogowiec (44,950 p.)
edycja 4 maja 2019 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
0 odpowiedzi 314 wizyt
0 głosów
1 odpowiedź 340 wizyt
pytanie zadane 20 września 2020 w C i C++ przez magda_19 Gaduła (3,080 p.)
0 głosów
1 odpowiedź 405 wizyt

92,417 zapytań

141,222 odpowiedzi

318,984 komentarzy

61,831 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

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 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!

...