Convert Bytes to Int / uint in C
Asked Answered
P

5

29

I have a unsigned char array[248]; filled with bytes. Like 2F AF FF 00 EB AB CD EF ..... This Array is my Byte Stream which I store my Data from the UART (RS232) as a Buffer.

Now I want to convert the bytes back to my uint16's and int32's.

In C# I used the BitConverter Class to do this. e.g:

byte[] Array = { 0A, AB, CD, 25 };
int myint1 = BitConverter.ToInt32(bytes, 0);
int myint2 = BitConverter.ToInt32(bytes, 4);
int myint3 = BitConverter.ToInt32(bytes, 8);
int myint4 = BitConverter.ToInt32(bytes, 12);
//...
enter code here
Console.WriteLine("int: {0}", myint1); //output Data...

Is there a similiar Function in C ? (no .net , I use the KEIL compiler because code is running on a microcontroller)

With Regards Sam

Petrapetracca answered 2/9, 2012 at 22:31 Comment(1)
This site uses a Question and Answer format - please do not edit the question to contain answers. Also the answer that you had edited into the question was wrong anyway.Noriega
P
19

Yes there is. Assume your bytes are in:

uint8_t bytes[N] = { /* whatever */ };

We know that, a 16 bit integer is just two 8 bit integers concatenated, i.e. one has a multiple of 256 or alternatively is shifted by 8:

uint16_t sixteen[N/2];

for (i = 0; i < N; i += 2)
    sixteen[i/2] = bytes[i] | (uint16_t)bytes[i+1] << 8;
             // assuming you have read your bytes little-endian

Similarly for 32 bits:

uint32_t thirty_two[N/4];

for (i = 0; i < N; i += 4)
    thirty_two[i/4] = bytes[i] | (uint32_t)bytes[i+1] << 8
        | (uint32_t)bytes[i+2] << 16 | (uint32_t)bytes[i+3] << 24;
             // same assumption

If the bytes are read big-endian, of course you reverse the order:

bytes[i+1] | (uint16_t)bytes[i] << 8

and

bytes[i+3] | (uint32_t)bytes[i+2] << 8
    | (uint32_t)bytes[i+1] << 16 | (uint32_t)bytes[i] << 24

Note that there's a difference between the endian-ness in the stored integer and the endian-ness of the running architecture. The endian-ness referred to in this answer is of the stored integer, i.e., the contents of bytes. The solutions are independent of the endian-ness of the running architecture since endian-ness is taken care of when shifting.

Protohuman answered 2/9, 2012 at 22:36 Comment(13)
then you shift in the least significant bits instead the most significant bits.Liston
@twall, I think you mean bytes.Rousseau
@sam, see my edit. Also, try reading a little on binary numbers. What I am doing is simple multiplication.Protohuman
stupid question, but my Array[100] is a char array, but does it matter because you guys wrote uint8_t bytes[N]Petrapetracca
@sam, if char on your system is signed, then yes, it matters. Imagine 0xFF. If you viewed as signed, it's -1. So instead of adding to 255, you will be subtracting by 1, which is not what you would want. unsigned char is the same as uint8_t by the way.Protohuman
I uploaded my Debug-Output s18.postimage.org/fipv7svp3/debugbyte.png when I tried Ethans way. It seems, that the other bytes will not fill ... now im trying your way.Petrapetracca
@sam, is RXBUFF your received bytes? The other solution doesn't work because they type of your array is int instead of unsigned char.Protohuman
yep RXBUFF are my recieved bytes (unsignec char array), but i can change the datatype of the array.Petrapetracca
OK guys it works with using a uint8_t BufferArray[] ... now it works fine! I testet it with the number 1985 (my birthday) as uint16 and uint32Petrapetracca
I'd cast each bytes[] to a uint32_t before shifting it. As written above, they'll be cast to ints before doing the shifts, but int need not be 32 bits.Erickericka
Your debug photo shows the type of RXBUFF as int[248]! @JoshuaGreen, you are right.Protohuman
Is it me, or am i the only one that's having to use a bit mask to make this work. For some reason my 2 inner bytes of a 4-byte int seem to be accumulating leading 1's when I bit shift them. For example, say little-endian byte position two is E1, (int)0xE1<<8 is evaluating to 4294959360 instead of 57600. ((int)0xE1<<8)&0x0000FF00 seems to correct it. What am I failing to recognize here?Karimakarin
@hydroparadise, I think you're not telling everything. Your number is probably stored in a char, which in your system is signed, so 0xE1 is actually a negative number. Casting that to int causes the sign extention to keep the signed value unmodified. The left shift has nothing to do with it.Protohuman
R
44

There's no standard function to do it for you in C. You'll have to assemble the bytes back into your 16- and 32-bit integers yourself. Be careful about endianness!

Here's a simple little-endian example:

extern uint8_t *bytes;
uint32_t myInt1 = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24);

For a big-endian system, it's just the opposite order:

uint32_t myInt1 = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];

You might be able to get away with:

uint32_t myInt1 = *(uint32_t *)bytes;

If you're careful about alignment issues.

Rousseau answered 2/9, 2012 at 22:36 Comment(7)
I'd cast each bytes[] to a uint32_t before shifting it. As written above, they'll be cast to ints before doing the shifts, but int need not be 32 bits.Erickericka
That would be safest, agreed.Rousseau
Assuming bytes is native endian, what sort of alignment issues would you run into? Or, I guess, what kind of situations makes alignment a problem?Aggregation
uint8_t and uint32_t are not compatible types and thus casting a uint8_t* to an uint32_t* violates the strict aliasing rule in C and thus inflicts undefined behaviour - and you mention it yourself...Pirn
@Aggregation Alignment issues have nothing to do with endianness: Its just that usually an 32bit word usually needs to be aligbed at a 32bit- boundary, while a byte (or Herr an arry of bytes...) might not be aligned that way. That's why the C standard prohibits casting a pointer to a byte to a pointer to a word...Pirn
*(uint32_t *)bytes causes undefined behaviour (strict aliasing violation)Noriega
Thank you! The best answer! You saved my day!Dialecticism
P
19

Yes there is. Assume your bytes are in:

uint8_t bytes[N] = { /* whatever */ };

We know that, a 16 bit integer is just two 8 bit integers concatenated, i.e. one has a multiple of 256 or alternatively is shifted by 8:

uint16_t sixteen[N/2];

for (i = 0; i < N; i += 2)
    sixteen[i/2] = bytes[i] | (uint16_t)bytes[i+1] << 8;
             // assuming you have read your bytes little-endian

Similarly for 32 bits:

uint32_t thirty_two[N/4];

for (i = 0; i < N; i += 4)
    thirty_two[i/4] = bytes[i] | (uint32_t)bytes[i+1] << 8
        | (uint32_t)bytes[i+2] << 16 | (uint32_t)bytes[i+3] << 24;
             // same assumption

If the bytes are read big-endian, of course you reverse the order:

bytes[i+1] | (uint16_t)bytes[i] << 8

and

bytes[i+3] | (uint32_t)bytes[i+2] << 8
    | (uint32_t)bytes[i+1] << 16 | (uint32_t)bytes[i] << 24

Note that there's a difference between the endian-ness in the stored integer and the endian-ness of the running architecture. The endian-ness referred to in this answer is of the stored integer, i.e., the contents of bytes. The solutions are independent of the endian-ness of the running architecture since endian-ness is taken care of when shifting.

Protohuman answered 2/9, 2012 at 22:36 Comment(13)
then you shift in the least significant bits instead the most significant bits.Liston
@twall, I think you mean bytes.Rousseau
@sam, see my edit. Also, try reading a little on binary numbers. What I am doing is simple multiplication.Protohuman
stupid question, but my Array[100] is a char array, but does it matter because you guys wrote uint8_t bytes[N]Petrapetracca
@sam, if char on your system is signed, then yes, it matters. Imagine 0xFF. If you viewed as signed, it's -1. So instead of adding to 255, you will be subtracting by 1, which is not what you would want. unsigned char is the same as uint8_t by the way.Protohuman
I uploaded my Debug-Output s18.postimage.org/fipv7svp3/debugbyte.png when I tried Ethans way. It seems, that the other bytes will not fill ... now im trying your way.Petrapetracca
@sam, is RXBUFF your received bytes? The other solution doesn't work because they type of your array is int instead of unsigned char.Protohuman
yep RXBUFF are my recieved bytes (unsignec char array), but i can change the datatype of the array.Petrapetracca
OK guys it works with using a uint8_t BufferArray[] ... now it works fine! I testet it with the number 1985 (my birthday) as uint16 and uint32Petrapetracca
I'd cast each bytes[] to a uint32_t before shifting it. As written above, they'll be cast to ints before doing the shifts, but int need not be 32 bits.Erickericka
Your debug photo shows the type of RXBUFF as int[248]! @JoshuaGreen, you are right.Protohuman
Is it me, or am i the only one that's having to use a bit mask to make this work. For some reason my 2 inner bytes of a 4-byte int seem to be accumulating leading 1's when I bit shift them. For example, say little-endian byte position two is E1, (int)0xE1<<8 is evaluating to 4294959360 instead of 57600. ((int)0xE1<<8)&0x0000FF00 seems to correct it. What am I failing to recognize here?Karimakarin
@hydroparadise, I think you're not telling everything. Your number is probably stored in a char, which in your system is signed, so 0xE1 is actually a negative number. Casting that to int causes the sign extention to keep the signed value unmodified. The left shift has nothing to do with it.Protohuman
C
4

In case of little-endian, can't you just use memcpy?

memcpy((char*)&myint1, aesData.inputData[startindex], length);
Career answered 3/7, 2017 at 8:47 Comment(1)
The local machine isn't always little-endian.Lenzi
F
0
            char letter = 'A';
            size_t filter = letter;
            filter = (filter <<  8 | filter);
            filter = (filter << 16 | filter);
            filter = (filter << 32 | filter);
            printf("filter: %#I64x \n", filter); 

result: "filter: 0x4141414141414141"

Floria answered 10/3, 2017 at 13:0 Comment(0)
H
0

My favourite but UB:

char bytes[] = {0};
sprintf(bytes, "%s", "\xAA\xBB\xCC");
uint64_t n = *(uint64_t*)bytes; // n = 0xccbbaa on little-endian
Helmholtz answered 3/12, 2023 at 10:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.