views:

1418

answers:

6

Hi, in a C program I have an long* that I want to serialize (thus converting to chars). A long doesn't fit in a single char, and the size varies depending of the processor (can be 4 bytes or 8 bytes).

Theres a good way to make the serialization and de-serialization?

A: 

If you create a char pointer that points to the beginning of the long array, when you increment through the char "array", you'll get 8 bits at a time. Be aware, though, that the long won't be null-terminated (necessarily, it might be), so you need to keep track where the end of it is.

For example:

long list[MAX];
char *serial = list;
int chunk = sizeof(long);
int k;
for(k=0; k<(MAX*chunk); k++){
  // do something with the "char"
}
warren
+1  A: 
long * longs;

// ...

int numChars = numLongs * sizeof(long);
char* longsAsChars = (char*) longs;
char* chars = malloc(numChars);
memcpy(chars, longsAsChars, numChars);
Lev
That does not work across different architectures.
Martin York
Why not? I think it should always work according to the specs.
Lev
since when is 'new' allowed in C?
Nicholas Mancuso
Assuming you are serialising to send across a network. The computer on the other end may have a different size of long. The long may have a different endianess etc.
Martin York
Architectures: the author didn't really want to serialize, just convert it to bytes in memory. new: can't argue with that.
Lev
@Lev: Why even do the copy then. Just cast the pointer to long*. All done. In the above code just pass 'longAsChars' as the result.
Martin York
A: 

In C you can get the size of a long with

sizeof(long)

But if your stored long has to be transferable between multiple platforms you should serialize it always as 4 bytes. Larger numbers couldn't be read by the 4byte processor anyway.

Georg
+1  A: 

You are likely solving the wrong problem. You should serialize to a fixed size int, using int32_t for instance. You probably want to use this fixed size type throughout your program, or you'll have problems when a 64-bit program can't save to the smaller size (or use int64_t).

If know you'll never have to load 64-bit saves on a 32-bit platform, then don't bother. Just write out sizeof(long) bytes to the file, and read back sizeof(long) bytes. But put a flag early in your data that indicates the source platform to avoid mistakes.

+1  A: 

You don't have to serialize as chars - you can fwrite as longs (to a file). To serialise to a char array invest a byte at the beginning to indicate the size of int and the byte order - you will need this later.

i.e.

char *p = &long_array[0];

To access the long array as char simply cast it - and multiple the length of the array by sizeof(long) to get the size in chars.

A simple example illustrates this:

#include <stdio.h>

main()
{
    int aaa[10];
    int i;
    char *p;

    for(i=0;i<sizeof(aaa)/sizeof(aaa[0]);i++)
    {
        aaa[i] = i;
        printf ("setting aaa[%d] = %8x\n",i,aaa[i]);
    }

    aaa[9] = 0xaabbccdd;

    printf ("sizeof aaa (bytes) :%d\n",sizeof(aaa));
    printf ("each element of aaa bytes :%d\n",sizeof(aaa[0]));

    p = (char*) aaa;
    for(i=0;i<sizeof(aaa);i++)
        printf ("%d: %8x\n",i,(unsigned char)p[i]);
}
Richard Harrison
+1  A: 

This is portable, but nowhere near as inefficient as using printf/scanf

void longtochar(char *buffer, unsigned long number) {
    int i;
    for (i=0; i<sizeof(long); i++) {
        buffer[i] = number & 0xFF; // place bottom 8 bits in char
        number = number >> 8; // shift down remaining bits
    }
    return; // the long is now stored in the first few (2,4,or 8) bytes of buffer
}

And to unpack it again (assuming long is the same size)

long chartolong(char *buffer) {
    long number = 0;
    int i;
    for (i=sizeof(long)-1; i>=0; i--) {
        number = number << 8; // left shift bits in long already
        number += buffer[i]; // add in bottom 8 bits
    }
    return number;
}

Do note the BIG assumption that long is the same length on both systems. Safe thing to do is #include <stdint.h> and use the types it provides (uint32_t or uint16_t).

Also, my code has it as an unsigned long. I don't have access to a C compiler right now, so I can't confirm if it would or not would not work with signed integers. If memory serves me, the behavior of it might be undefined (though it might not matter, how I handle it).

PhirePhly