views:

156

answers:

7

I have a stucture:

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

I need to write it to binary file.

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

I fill my structure with data and then i write in to the file:

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

In my file doesnt exist a name and surname, it have an adresses of char*. How can i write to file a word, not an adresses?

A: 

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);
Raul Agrait
This won't work with the posted structure definition - `sizeof(s->name)` will give the size of a `char *`.
caf
Yup, you're right -- I changed the sizeof to strlen
Raul Agrait
so now you need to change it to strlen() * sizeof(char)...(Well, sizeof(char) will be 1, just for the principle.
rursw1
+4  A: 

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);
fwrite(&(s->age), sizeof(s->age), 1, fp); //This one is a bit dangerous, but you get the idea.
Williham Totland
but i have about 10 fields in the structure. its very long code
Sergey Gavruk
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.
Williham Totland
@Sergey: you can encapsulate these operations into a single function, so it doesn't have to be *"very long code"*.
Paul R
unwind
@unwind: Of course; fixing
Williham Totland
+1  A: 

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.

SysAdmin
-1: Fixed string max lengths are, as has been remarked, bad.
Williham Totland
@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?
SysAdmin
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
@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.
SysAdmin
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
@caf: If you have 15,000 student records, you probably want to be using a database, like, say, SQLite.
Williham Totland
+2  A: 

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;
Paul R
-1: Fixed string max lengths are bad. Very bad. (Mmmmmkay?)
Williham Totland
@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"*.
Paul R
The [homework] tag appeared well after I gave my answer and made my comment. In any case, [homework] doesn't excuse sloppy code.
Williham Totland
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?)
caf
@caf: In a file system, *everything* is fixed size. This is because read overhead **must** be kept to an absolute minimum. This is not a file system implementation. It's a userland application. And while it surely isn't common for people to have names of length 500; it's possible, and indeed plausible for the length of names to fluctuate wildly between, say, 2 characters and, I would say, at least 50. (Three first names aren't unheard of.) That adds up to either a lot of wasted space, or occasional name overflows. And not being able to store someones full name? That is *wildly* unacceptable.
Williham Totland
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)!
caf
@caf: At the point where the students is used for lookup and searched as a random access file; you need to use something else than a simple serial format, like a database (SQLite should fit the bill nicely).
Williham Totland
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.
caf
A: 

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.

rursw1
A: 

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);
...
Scott Wales
+1  A: 

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.

Joey Adams