22
votes

I want to store a 4-byte int in a char array... such that the first 4 locations of the char array are the 4 bytes of the int.

Then, I want to pull the int back out of the array...

Also, bonus points if someone can give me code for doing this in a loop... IE writing like 8 ints into a 32 byte array.

int har = 0x01010101;
char a[4];
int har2;

// write har into char such that:
// a[0] == 0x01, a[1] == 0x01, a[2] == 0x01, a[3] == 0x01 etc.....

// then, pull the bytes out of the array such that:
// har2 == har

Thanks guys!

EDIT: Assume int are 4 bytes...

EDIT2: Please don't care about endianness... I will be worrying about endianness. I just want different ways to acheive the above in C/C++. Thanks

EDIT3: If you can't tell, I'm trying to write a serialization class on the low level... so I'm looking for different strategies to serialize some common data types.

10
Maybe you should do your own homework... And then, if you have any doubts, you can post your code here and we will try to help you then. If you don't try to do it yourself, you are not going to learn anything. - jpmelos
If you were writing C, you would know better than to initialize a variable with a value. - jkeys
ummmm what? The above is just to get the question across. - Polaris878
Are you only worried about ints, or do you need to do the same with non-POD types as well? - jalf
Actually I should only be dealing with POD types (I have a lot of terrain data that I'm sending across a network). Hopefully I won't be dealing with anything too complicated. - Polaris878

10 Answers

41
votes

Unless you care about byte order and such, memcpy will do the trick:

memcpy(a, &har, sizeof(har));
...
memcpy(&har2, a, sizeof(har2));

Of course, there's no guarantee that sizeof(int)==4 on any particular implementation (and there are real-world implementations for which this is in fact false).

Writing a loop should be trivial from here.

23
votes

Not the most optimal way, but is endian safe.


int har = 0x01010101;
char a[4];
a[0] = har & 0xff;
a[1] = (har>>8)  & 0xff;
a[2] = (har>>16) & 0xff;
a[3] = (har>>24) & 0xff;
9
votes

Note: Accessing a union through an element that wasn't the last one assigned to is undefined behavior. (assuming a platform where characters are 8bits and ints are 4 bytes) A bit mask of 0xFF will mask off one character so

char arr[4];
int a = 5;

arr[3] = a & 0xff;
arr[2] = (a & 0xff00) >>8;
arr[1] = (a & 0xff0000) >>16;
arr[0] = (a & 0xff000000)>>24;

would make arr[0] hold the most significant byte and arr[3] hold the least.

edit:Just so you understand the trick & is bit wise 'and' where as && is logical 'and'. Thanks to the comments about the forgotten shift.

8
votes
#include <stdio.h>

int main(void) {
    char a[sizeof(int)];
    *((int *) a) = 0x01010101;
    printf("%d\n", *((int *) a));
    return 0;
}

Keep in mind:

A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned for the pointed-to type, the behavior is undefined.

8
votes
int main() {
    typedef union foo {
        int x;
        char a[4];
    } foo;

    foo p;
    p.x = 0x01010101;
    printf("%x ", p.a[0]);
    printf("%x ", p.a[1]);
    printf("%x ", p.a[2]);
    printf("%x ", p.a[3]);

    return 0;
}

Bear in mind that the a[0] holds the LSB and a[3] holds the MSB, on a little endian machine.

7
votes

Don't use unions, Pavel clarifies:

It's U.B., because C++ prohibits accessing any union member other than the last one that was written to. In particular, the compiler is free to optimize away the assignment to int member out completely with the code above, since its value is not subsequently used (it only sees the subsequent read for the char[4] member, and has no obligation to provide any meaningful value there). In practice, g++ in particular is known for pulling such tricks, so this isn't just theory. On the other hand, using static_cast<void*> followed by static_cast<char*> is guaranteed to work.

– Pavel Minaev

4
votes

You can also use placement new for this:

void foo (int i) {
  char * c = new (&i) char[sizeof(i)];
}
2
votes

    #include <stdint.h>

    int main(int argc, char* argv[]) {
        /* 8 ints in a loop */
        int i;
        int* intPtr
        int intArr[8] = {1, 2, 3, 4, 5, 6, 7, 8};
        char* charArr = malloc(32);

        for (i = 0; i < 8; i++) {
            intPtr = (int*) &(charArr[i * 4]);
          /*  ^            ^    ^        ^     */
          /* point at      |    |        |     */
          /*       cast as int* |        |     */
          /*               Address of    |     */
          /*            Location in char array */

            *intPtr = intArr[i]; /* write int at location pointed to */
        }

        /* Read ints out */
        for (i = 0; i < 8; i++) {
            intPtr = (int*) &(charArr[i * 4]);
            intArr[i] = *intPtr;
        }

        char* myArr = malloc(13);
        int myInt;
        uint8_t* p8;    /* unsigned 8-bit integer  */
        uint16_t* p16;  /* unsigned 16-bit integer */
        uint32_t* p32;  /* unsigned 32-bit integer */

        /* Using sizes other than 4-byte ints, */
        /* set all bits in myArr to 1          */
        p8 = (uint8_t*) &(myArr[0]);
        p16 = (uint16_t*) &(myArr[1]);
        p32 = (uint32_t*) &(myArr[5]);
        *p8 = 255;
        *p16 = 65535;
        *p32 = 4294967295;

        /* Get the values back out */
        p16 = (uint16_t*) &(myArr[1]);
        uint16_t my16 = *p16;

        /* Put the 16 bit int into a regular int */
        myInt = (int) my16;

    }

1
votes
char a[10];
int i=9;

a=boost::lexical_cast<char>(i)

found this is the best way to convert char into int and vice-versa.

alternative to boost::lexical_cast is sprintf.

char temp[5];
temp[0]="h"
temp[1]="e"
temp[2]="l"
temp[3]="l"
temp[5]='\0'
sprintf(temp+4,%d",9)
cout<<temp;

output would be :hell9

0
votes
union value {
   int i;
   char bytes[sizof(int)];
};

value v;
v.i = 2;

char* bytes = v.bytes;