28

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

M.M
  • 138,810
  • 21
  • 208
  • 365
Sam
  • 295
  • 1
  • 3
  • 7
  • 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. – M.M Nov 10 '18 at 22:10

4 Answers4

43

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.

Carl Norum
  • 219,201
  • 40
  • 422
  • 469
  • 4
    I'd cast each `bytes[]` to a `uint32_t` before shifting it. As written above, they'll be cast to `int`s before doing the shifts, but `int` need not be 32 bits. – Joshua Green Sep 03 '12 at 03:35
  • That would be safest, agreed. – Carl Norum Sep 03 '12 at 04:29
  • 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? – Azmisov Jun 24 '15 at 04:56
  • `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... – Michael Beer Jun 22 '18 at 22:24
  • @Azmisov 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... – Michael Beer Jun 22 '18 at 22:32
  • 1
    `*(uint32_t *)bytes` causes undefined behaviour (strict aliasing violation) – M.M Nov 10 '18 at 22:11
  • 1
    Thank you! The best answer! You saved my day! – imike Jul 31 '21 at 11:40
20

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.

Shahbaz
  • 46,337
  • 19
  • 116
  • 182
  • then you shift in the least significant bits instead the most significant bits. – devsnd Sep 02 '12 at 22:40
  • @twall, I think you mean bytes. – Carl Norum Sep 02 '12 at 22:41
  • @sam, see my edit. Also, try reading a little on binary numbers. What I am doing is simple multiplication. – Shahbaz Sep 02 '12 at 22:43
  • stupid question, but my Array[100] is a char array, but does it matter because you guys wrote uint8_t bytes[N] – Sam Sep 02 '12 at 22:47
  • @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. – Shahbaz Sep 02 '12 at 22:49
  • I uploaded my Debug-Output http://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. – Sam Sep 02 '12 at 23:21
  • @sam, is `RXBUFF` your received bytes? The other solution doesn't work because they type of your array is `int` instead of `unsigned char`. – Shahbaz Sep 02 '12 at 23:24
  • yep RXBUFF are my recieved bytes (unsignec char array), but i can change the datatype of the array. – Sam Sep 02 '12 at 23:33
  • 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 uint32 – Sam Sep 02 '12 at 23:44
  • I'd cast each `bytes[]` to a `uint32_t` before shifting it. As written above, they'll be cast to `int`s before doing the shifts, but `int` need not be 32 bits. – Joshua Green Sep 03 '12 at 03:34
  • Your debug photo shows the type of `RXBUFF` as `int[248]`! @JoshuaGreen, you are right. – Shahbaz Sep 03 '12 at 07:03
  • 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? – Chad Harrison Apr 04 '14 at 12:54
  • @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. – Shahbaz Apr 04 '14 at 18:55
5

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

memcpy((char*)&myint1, aesData.inputData[startindex], length);
1
            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"

Andre
  • 172
  • 2
  • 8