4

I'm trying to convert an unsigned char array buffer into a signed int (vice versa).

Below is a demo code:

int main(int argv, char* argc[])
{
    int original = 1054;
    unsigned int i = 1054;
    unsigned char c[4];
    int num;

    memcpy(c, (char*)&i, sizeof(int));

    //num  = *(int*) c;                          // method 1 get
    memcpy((char *)&num, c, sizeof(int));        // method 2 get
    printf("%d\n", num);

    return 0;
}

1) Which method should I use to get from unsigned char[] to int?

method 1 get or method 2 get? (or any suggestion)

2) How do I convert the int original into an unsigned char[]?

I need to send this integer via a buffer that only accepts unsigned char[]

Currently what i'm doing is converting the int to unsigned int then to char[], example :

int g = 1054;
unsigned char buf[4];
unsigned int n;
n = g;
memcpy(buf, (char*)&n, sizeof(int));

Although it works fine but i'm not sure if its the correct way or is it safe?

PS. I'm trying to send data between 2 devices via USB serial communication (between Raspberry Pi & Arduino)

Doe Joe
  • 127
  • 1
  • 3
  • 11

2 Answers2

4

Below approach will work regardless of endianness on machines (assuming sizeof(int)==4):

unsigned char bytes[4];
unsigned int n = 45;

bytes[3] = (n >> 24) & 0xFF;
bytes[2] = (n >> 16) & 0xFF;
bytes[3] = (n >> 8) & 0xFF;
bytes[0] = n & 0xFF;

Above code converts integer to byte array in little endian way. Here is link also with more information.

For reverse operation, see the answers here.

The approach you have with memcpy may give different results on different computers. Because memcpy will copy whatever is there in source address to destionation, and depending if computer is little endian or big endian, there maybe a LSB or MSB at the starting source address.

Community
  • 1
  • 1
Giorgi Moniava
  • 27,046
  • 9
  • 53
  • 90
  • 2
    Upvoted, although all those `& 0xff` are not actually necessary (but they may add something in clarity). – Matteo Italia Feb 27 '16 at 10:12
  • Using your code, it gives me result in another endianness (c array being the one I'm currently using and is working, bytes array being the one you suggested): c[i] = 0x2d c[i] = 0x0 c[i] = 0x0 c[i] = 0x0 bytes[i] = 0x0 bytes[i] = 0x0 bytes[i] = 0x0 bytes[i] = 0x2d – Doe Joe Feb 27 '16 at 10:14
  • @DoeJoe You can try little endian way - I will modify – Giorgi Moniava Feb 27 '16 at 10:15
  • 2
    @Somebody union will give different result on different machine to reasons similar of memcpy – Giorgi Moniava Feb 27 '16 at 10:18
  • @MatteoItalia thanks-I will leave 0xFF, anyway, at least they don't hurt – Giorgi Moniava Feb 27 '16 at 10:19
  • 1
    @Giorgi just curious, why is it that little endian way works irregardless of endianness on machines? i tried testing via bluetooth on android device and the raspberry pi and big endian way works while little endian way doesn't work. just curious about all these endianness hah – Doe Joe Feb 27 '16 at 10:26
  • 2
    @DoeJoe It is not that little endian works, and big doesn't. It is the method. The method I mentioned, if you encode integer in little endian way using that method, and apply a similar reverse operation (recover integer from byte array assuming bytes are in little endian way), on a different machine, you will get same result. The same approach will also work on any machine, even if you convert the integer in big endian way. Now it maybe your device expects integer in some endianness, that is different thing. You may want to do bit more research on endianness. – Giorgi Moniava Feb 27 '16 at 10:29
  • @Giorgi ok I got what you mean. Thanks a lot! – Doe Joe Feb 27 '16 at 10:32
  • @DoeJoe you are welcome. read about endianness, and make sure you get why this method is portable, while others not. – Giorgi Moniava Feb 27 '16 at 10:34
3

You could store both int (or unsigned int) and unsigned char array as union. This method is called type punning and it is fully sanitized by standard since C99 (it was common practice earlier, though). Assuming that sizeof(int) == 4:

#include <stdio.h>

union device_buffer {
    int i;
    unsigned char c[4];
};

int main(int argv, char* argc[])
{
    int original = 1054;

    union device_buffer db;
    db.i = original;

    for (int i = 0; i < 4; i++) {
        printf("c[i] = 0x%x\n", db.c[i]);
    }
}

Note that values in array are stored due to byte order, i.e. endianess.

Grzegorz Szpetkowski
  • 36,988
  • 6
  • 90
  • 137
  • i see somewhere on stackoverflow that memcpy and union may produce different results! Is that true? And what is the difference between union and struct? Thanks a lot! – Doe Joe Feb 27 '16 at 10:05
  • 1
    Notice that type punning via union is still UB in C++; the universally safe way remains `memcpy`. – Matteo Italia Feb 27 '16 at 10:09
  • @DoeJoe: char was my typo, now it's correct. To your second question, the result of memcpy and union should be exactly the same as far as you are running your code on the same machine. – Grzegorz Szpetkowski Feb 27 '16 at 10:13
  • can i check with you, is it necessary for me to change the signed int to unsigned int before placing in buffer or is that totally unnecessary at all? – Doe Joe Feb 27 '16 at 10:31
  • 1
    @DoeJoe: It wouldn't matter unless you are storing values outside the range of `int`. Anyway, I changed to `unsigned`, because that way it was in your question. – Grzegorz Szpetkowski Feb 27 '16 at 10:53
  • 2
    if int is shorter than 4 chars then this is ub: `db.i = original;` left remaining chars uninitialized. – user3528438 Feb 27 '16 at 13:03