-2

I recently started doing a small project in C to learn how to properly handle working with output and input for a file.

The situation is as follows:
When I use a char array all works fine, but when I pass a struct as a parameter to fwrite() the output goes crazy.

FILE *fp;
Acazzo a;
fp = fopen("Inserimento.dat","w");
gets(a.nome);
fflush(stdin);
gets(a.cognome);
fflush(stdin);
gets(a.note);
fflush(stdin);
fwrite(&a,sizeof(a),1,fp);
fclose(fp);
return 0;

That's a really short project for learning, so I skipped some stuff like printf for name surname etc etc.
This is the output I get when I open the file

GIulio ÿÿÿÿSavoca E' un ottimo student

Andrei T
  • 2,985
  • 3
  • 21
  • 28
Wallcraft
  • 21
  • 1
  • 8
  • What output did you expect? –  Oct 11 '15 at 11:39
  • 1
    First, stop using `gets`, it's dangerous, deprecated since long and even removed from the latest C standard. Second, don't call `fflush` on `stdin`, it's technically undefined in the C specification. – Some programmer dude Oct 11 '15 at 11:40
  • 3
    What is the definition of your `Acazzo` structure? – S.Clem Oct 11 '15 at 11:41
  • Sry for the mistake out of standard, but im a student i don't know all the standard q.q Acazzo definition is this typedef struct{ char name[20]; char surname[20]; char note[30]; }Acazzo; – Wallcraft Oct 11 '15 at 11:44
  • 1
    @Wallcraft just read [`gets(3)`](http://man7.org/linux/man-pages/man3/gets.3.html). There's a reason why it says **never use this function** and it is explained. –  Oct 11 '15 at 11:45
  • So i need to use fgets()? that will fix my output problem? – Wallcraft Oct 11 '15 at 11:48
  • @Wallcraft no, it will fix the possible buffer overflow / crash / security hole coming with `gets()`. Your output is just correct, read my answer about that. –  Oct 11 '15 at 11:49

1 Answers1

2

First there's nothing "crazy" about that output. Don't expect any "magic" happening, writing a struct to a file just does write exactly how the struct is stored in RAM, byte by byte.

Second, that's almost never what you want, because the layout in memory for a struct will differ from machine to machine, sometimes even from compiler to compiler. For writing structs to a file and reading them back reliably, you have to do some sort of (de-)serialization using only textual representations of all individual fields.


Very basic example of (de-)serializing a struct:

This example is really minimal and stores a struct in a file line-by-line, assuming the struct and all pointer fields are allocated dynamically. I didn't test this code, it might have a bug, just to show a simple idea.

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

typedef struct data
{
    int    id;
    char   *name;
    char   *value;
} data;

static char *copyInput(char *buf)
{
    char *copy;
    size_t endpos = strlen(buf) - 1;
    while (buf[endpos] == '\r' || buf[endpos] == '\n')
    {
        buf[endpos] = 0;
        --endpos;
    }

    copy = malloc(strlen(buf) + 1);
    strcpy(copy, buf);
    return copy;
}

data *data_deserialize(FILE *fp)
{
    char buf[256];
    data *d = calloc(1, sizeof(*d));
    if (!d) return 0;

    if (!fgets(buf, 256, fp)) goto err;
    d->id = atoi(buf);
    if (!fgets(buf, 256, fp)) goto err;
    d->name = copyInput(buf);
    if (!fgets(buf, 256, fp)) goto err;
    d->value = copyInput(buf);

    return d;

err:
    free(d->name);
    free(d->value);
    free(d);
    return 0;
}

int data_serialize(FILE *fp, const data *d)
{
    int rc;

    rc = fprintf(fp, "%d\n", d->id);
    if (rc < 0) return rc;

    rc = fprintf(fp, "%s\n", d->name);
    if (rc < 0) return rc;

    rc = fprintf(fp, "%s\n", d->value);

    return rc;
}
  • 1
    And the memory representation can change from build options to others. By using some "speed" options, the compiler can decide to align your structure members to 16, 32 or 64 bits in order to optimize memory access. – S.Clem Oct 11 '15 at 11:51
  • You mean that i need to convert my struct into a string or something else? – Wallcraft Oct 11 '15 at 11:53
  • @Wallcraft there are lots of possibilities, with all fixed-size members, you could get away with just writing them individually. For more complex scenarios, you need more complex (de-)serialization strategies. There are libraries for common textual formats like xml, json, ini, ... –  Oct 11 '15 at 11:55
  • @Wallcraft [here](http://stackoverflow.com/questions/16543519/serialization-of-struct) is a post with an example of serialization that could help you. – S.Clem Oct 11 '15 at 12:02
  • That goes over my knowledge, can u give me some tips to (de-)serialize my struct? Not the whole code, just a tip – Wallcraft Oct 11 '15 at 12:02
  • @S.Clem well that is in [tag:c++] ... but it should show some concepts. –  Oct 11 '15 at 12:03
  • @Wallcraft adding a *very* basic example –  Oct 11 '15 at 12:04
  • @Wallcraft example added. But understand you can do anything that might fit the purpose. –  Oct 11 '15 at 12:21
  • @FelixPalmen very good basic example. Just missing a little memset to 0 (zero) of the data structure after the `if (!d) return 0;` to get the code after `err:` to work correctly. ;) – S.Clem Oct 11 '15 at 12:27
  • @S.Clem uhm ... that's why I used `calloc()` instead of `malloc()` ;) was thinking about that ... –  Oct 11 '15 at 12:29
  • Nothing, im really bad. Is too hard for me implements this and i have no time for study how implements, this project is ofr tomorrow. – Wallcraft Oct 11 '15 at 14:50