6

I have a structure:

typedef struct student {
  char *name;
  char *surname;
  int age;
} Student;

I need to write the structure into a binary file.
Here is my attempt :

Student *s = malloc(sizeof(*s));

I fill my structure with data and then I write the struct into the file with :

fwrite(s, sizeof(*s), 1, fp);

In my file, name and surname don't exist. Instead they have their respective address of their char * pointer.
How can I write a char * into a file instead of the address of the pointer ?

7 Answers 7

11

You need to dereference your pointer and write the parts of the struct. (You should not fwrite a struct directly, but rather encode its parts and write those:

Student *s = malloc(sizeof(*s));
s->name = "Jon";
s->surname = "Skeet";
s->age = 34;

// ....

fwrite(s->name, sizeof(char), strlen(s->name) + 1, fp);
fwrite(s->surname, sizeof(char), strlen(s->surname) + 1, fp);

//This one is a bit dangerous, but you get the idea.
fwrite(&(s->age), sizeof(s->age), 1, fp); 
Sign up to request clarification or add additional context in comments.

4 Comments

but i have about 10 fields in the structure. its very long code
Then you write a function that writes all of it; and just call that; like so: int fwriteStudent(Student *s, FILE *fp);, and just remember to keep that function updated as you modify your struct definition.
@Sergey: you can encapsulate these operations into a single function, so it doesn't have to be "very long code".
You probably mean &s->age, otherwise it's even more dangerous, and extremely unlikely to produce the deisired result.
4

You will have to do some extra work to write out the data pointed to by the structure elements - probably just write it out element by element.

Alternatively change your structure to something like this:

typedef struct
{
    char name[MAX_NAME_LEN];
    char surname[MAX_SURNAME_LEN];
    int age;
} Student;

7 Comments

@Williham: fixed string lengths are fine in certain cases - for a homework problem with a small data set it's perfectly appropriate to use them. Using a packed, but more complex representation is premature optimisation, which is, to use your words, "very bad".
The [homework] tag appeared well after I gave my answer and made my comment. In any case, [homework] doesn't excuse sloppy code.
Fixed size fields for things like human names are perfectly acceptable - after all, there is certainly a reasonable upper bound on them (is it reasonable to expect a human with a name longer than 500 characters?). As long as the code is detecting and properly dealing with attempts to store too-long input, then it is fine. (Are you aware that there's a maximum allowed length of a filename on your favourite filesystem? Can you guess what the underlying data structure looks like?)
The wasted space is a speed/space tradeoff - disk space is cheap; cheaper than CPU cycles. Not being able to store someone's full name is certainly a failure, and I would absolutely advocate a very conservative approach to that upper bound - but to argue that there isn't a reasonable upper bound is silly. After all, the chance of a 500 character name being entered without any errors is almost nil. Futhermore, dismal performance in a userland application can be just as much a problem as it is in a filesystem (there's a reason why people use VARCHAR2 in Oracle rather than CLOB everywhere)!
Perhaps, but then you might as well just say "don't use fwrite, use SQLite". If there's a need to do it using fwrite, then it could be that fixed-sized text fields are appropriate. Your blanket statement "Fixed string lengths are bad" is overly dogmatic, and simply wrong in many cases. It's a tradeoff, which means sometimes it will be the right answer and sometimes not.
|
3

if you want to directly write the structure as it is to a file you would want to define the sizes of name and surname rather than dynamically allocating it.

typedef structure student { char name[100]; char surname[100]; int age; } 

else you will need to write each info in the structure one by one.

5 Comments

@Williham Totland - I have given a reason y you would do this. i.e if you want to write the structure as it is. dont just blindly remarking this as bad. Do you have any better idea, if you want to write the structure in one fwrite operation?
Writing the struct as one fwrite-operation is, again as has been remarked, a Bad Idea™. It is almost, but not quite, entirely non-portable, and doesn't always yield the desired results. Overflowable strings are a security breach waiting to happen.
@Williham Totland - "it is non-portable, and does'nt always desired result" - In C this is more portable than writing each structure fields one by one. And other issues like security breach occurs just because there is no validation or just that the code you have written is plain wrong.
Having a fixed size structure can certainly be appropriate in some cases - for example, if we are writing 15,000 of these student records to a file, then we could quickly seek to the 9,347th one if they are all the same size. If each one is a variable size, then we must read all 9,346 preceeding structures first (or have a separate index structure).
@caf: If you have 15,000 student records, you probably want to be using a database, like, say, SQLite.
3

In C, you have to serialize structures (convert them to a series of bytes) manually. If you want the data you output to be readable by another machine, you have to take endianness account endianness.

Here's a simple example of serializing your structure (writing it to a file), disregarding endianness/differing word size and making the code unportable:

size_t length;

length = strlen(s->name) + 1;
fwrite(&length, sizeof(length), 1, fp);
fwrite(s->name, 1, length, fp);

length = strlen(s->surname) + 1;
fwrite(&length, sizeof(length), 1, fp);
fwrite(s->surname, 1, length, fp);

fwrite(&s->age, sizeof(s->age), 1, fp);

And to unserialize:

size_t length;

fread(&length, sizeof(length), 1, fp);
s->name = malloc(length);
fread(s->name, 1, length, fp);

fread(&length, sizeof(length), 1, fp);
s->surname = malloc(length);
fread(s->surname, 1, length, fp);

fread(&s->age, sizeof(s->age), 1, fp);

In a real application, you should check the validity of your input rather than blindly assuming the input is valid and trustworthy. Also, you need to decide what byte order you'll use to store your ints, size_ts, etc, and make sure you read/write them in the correct byte order by using bit shifting.

By the way, you may want to look at tpl, a simple binary serialization library for C.

Comments

0

You need to dereference s and individually write out each element of the structure to actually write thecontents to the file:

size_t nameCount = strlen(s->name) + 1;
fwrite(s->name, sizeof(char), nameCount, fp);

size_t surnameCount = strlen(s->surname) + 1;
fwrite(s->surname, sizeof(char), surnameCount, fp);

2 Comments

This won't work with the posted structure definition - sizeof(s->name) will give the size of a char *.
so now you need to change it to strlen() * sizeof(char)... (Well, sizeof(char) will be 1, just for the principle.
0

Note that anyway you won't be able to see the fields in a 'normal' form because you write it to a binary file. Try to open the file without the "b" in the mode argument of fopen (r+\ w\ a etc. instead of rb+\ wb\ ab).

You can see that when using fread (from the binary file, after opening it in "rb" mode you should get the structure fields as expected.

Comments

0

You'll have to write out the fields individually, as others have said. One thing which may help reading back the data is to write out the string lengths before each entry, eg.

size_t len;
len = strlen(s->name)+1;
fwrite(&len,sizeof(len),1,stream);
fwrite(s->name,sizeof(*(s->name)),len,stream);
...

Reading back is then a matter of

Student * s = malloc(sizeof(*s));
size_t len;
fread(&len,sizeof(len),1,stream);
s->name = malloc(len);
fread(s->name,sizeof(*(s->name)),len,stream);
...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.