tags:

views:

91

answers:

4

How can I "pack" and "write" a struct to a file using C so that:

struct a {
    uint64_t a;
    char* b;
    uint16_t c;
} a;
a b;
b.a = 3;
b.b = "Hello";
b.c = 4;

gets written to the file as

00 00 00 00 00 00 00 03 48 65 6c 6c 6f 00 00 04
+1  A: 

you must write your own way to serialize this data; the compiler won't hand you a built-in way to deal with the string. There are serialization libraries out there but I don't know any for straight C.

But, consider using a more structured method for serializing data, such as json or xml. Even an INI file is better than raw binary dump. Reasons for this are:

  • easier to debug
  • more forward compatible
  • less rigid / error-prone
  • if you use an existing library, then you reap the rewards of the community that goes with it.
  • an existing library is likely to support features that you will later scratch your head over like arrays
  • better cross platform compatibility.
tenfour
Stop to XML overusers!
adf88
it's just an example; i hate xml overuse as well, which I hoped to hint at with my INI comment. storage libs are everywhere, I'm just recommending picking one instead of writing raw bin.
tenfour
+9  A: 

In C, you'll have to code a function to do this for you. You can't just blat the structure out to disk because b is a pointer that makes no sense without the backing string. And, unless you know (and can control) how your compiler packs its structures, you're better off with a utility function anyway, even without pointers.

And, as if that wasn't enough, you should output the length of the string as well so you know how many bytes to read back.

You'll be looking for something like:

int better_than_blat (FILE *f, struct a *x) {
    size_t len = strlen (x->b);
    if (fwrite (&(x->a), sizeof(long), 1, f) != 1) return -1;
    if (fwrite (&len, sizeof(size_t), 1, f) != 1) return -1;
    if (fwrite (x->b, len, 1, f) != 1) return -1;
    if (fwrite (&(x->c), sizeof(short), 1, f) != 1) return -1;
    return 0;
}

int better_than_unblat (FILE *f, struct a *x) {
    size_t len;
    if (fread (&(x->a), sizeof(long), 1, f) != 1) return -1;
    if (fread (&len, sizeof(size_t), 1, f) != 1) return -1;
    x->b = malloc (len + 1);
    if (x->b == NULL) return -1;
    memset (x->b, 0, len + 1);
    if (fread (x->b, len, 1, f) != 1) return -1;
    if (fread (&(x->c), sizeof(short), 1, f) != 1) return -1;
    return 0;
}
paxdiablo
+1 for using "blat" correctly!
Matt Joiner
additionally, structure packaging is extremely platform/compiler dependant, even without the appearance of a char* pointer you'd need your own function to guarantee certain write outs.
KillianDS
A: 

Will the following help?

struct buffer {

  char bytes[1000];
  int nbytes;
};

struct buffer *new_buffer(){
  struct buffer b = (struct buffer*) malloc(sizeof(struct buffer));
  b->nbytes = 0;
  return b;
}

void append_long(struct buffer *b, long *l){
  memcpy(b->bytes + b->nbytes, l);
  b->nbytes += sizeof(*l);
}

// ...and so on for other types

void fwrite_buffer(FILE *fp, struct buffer *b){
  fwrite(b->bytes, sizeof(*b), 1, fp);
}

Usage:

struct buffer *buf = new_buffer();
struct a b;
b.a = 3;
b.b = "Hello";
b.c = 4;
append_long(buf, &(b.a));
append_pointer(buf, &(b.b));
append_short(buf, &(b.b));
fwrite_buffer(fp, buf);
Vulcan Eager
A: 

You can safely pack your structure into byte array, if you will not use pointers in it and will explicitly define packing alignment.

For example (gcc):

struct a {
    long a;
    char b[256];
    short c;
}  __attribute__((__packed__));

int size = sizeof(a);
void* buffer = malloc(size);
memcpy(buffer, (void*)a, size);
optio
This method is extremely fast and will give to you many problems in future. If you need simple and maintainable solution try to use ProtoBuffers or MessagePack. They are 10 times slower, but it's not a problem on practice.
optio