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)

7
  • 4
    Not sure if it's safe, but one thing to make it less unsafe is to say unsigned char buf[sizeof(int)] rather than unsigned char buf[4]. Commented Feb 27, 2016 at 9:48
  • 2
    method 1 may result in segfault if the char array is not properly aligned Commented Feb 27, 2016 at 9:55
  • num = *(int *)c don't do that, c is not required to be properly aligned for an int Commented Feb 27, 2016 at 9:57
  • Ok thanks for all suggestion! Shall stick to memcpy method! Commented Feb 27, 2016 at 10:03
  • 1
    *(int *)c also violates the strict aliasing rule, do not do this Commented Feb 27, 2016 at 10:32

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

9 Comments

Upvoted, although all those & 0xff are not actually necessary (but they may add something in clarity).
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
@Somebody union will give different result on different machine to reasons similar of memcpy
@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
@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.
|
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.

6 Comments

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!
Notice that type punning via union is still UB in C++; the universally safe way remains memcpy.
@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.
@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.
if int is shorter than 4 chars then this is ub: db.i = original; left remaining chars uninitialized.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.