0

I'm trying to typecast the uint8_t buffer to structure with bit field as below.

#include<stdio.h>
#include<inttypes.h>

struct msg{
    uint64_t field:56;
};

void main()
{
   uint8_t buf[8]={0x7,0x6,0x5,0x4,0x3,0x2,0x1};
   struct msg *m = buf;
   printf("buf=%"PRIx64"\n",m->field);
}

But I'm getting the output as below.

Actual output:

buf=1020304050607

Expected output:

buf=7060504030201

Am I doing anything wrong while typecasting?

Community
  • 1
  • 1
KBlr
  • 312
  • 1
  • 11
  • 6
    The undefined behavior aside... You seem to not be taking the endianess of your machine into account. – StoryTeller - Unslander Monica Sep 03 '18 at 09:18
  • @StoryTeller Can you please brief your comment? How it will lead to UB and how to consider endianess ? – KBlr Sep 03 '18 at 09:20
  • You're not actually type-casting, I would be very surprised if that compiled at all. That aside, the exact nature of how bitfields are implemented is implementation-defined, so don't do this. Write manual serialization/deserialization functions. – unwind Sep 03 '18 at 09:20
  • This is the warning you should get, warning: initialization from incompatible pointer type [-Wincompatible-pointer-types] – danglingpointer Sep 03 '18 at 09:22
  • 3
    You have undefined behavior because you violate [strict aliasing](https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule). And as for how to consider endianess, that's something that won't fit in a comment, nor am I the right person to write an answer to that effect. – StoryTeller - Unslander Monica Sep 03 '18 at 09:26
  • @unwind I don't have any option other than typecasting. – KBlr Sep 03 '18 at 09:26
  • Why put 7 bytes in an 8 byte buffer? – Paul Ogilvie Sep 03 '18 at 09:27
  • You have. You can and you should `m.field = buf[0]<<48|buf[1]<<40|buf[2]<<32|....|buf[7]<<0;` – KamilCuk Sep 03 '18 at 09:58
  • @StoryTeller Thank you for briefing! – KBlr Sep 03 '18 at 10:04
  • @KamilCuk I was taking about following strict aliasing rule.Are you taking about handling endianess? – KBlr Sep 03 '18 at 10:06
  • Both. Writing your own serialization functions is the only "good", valid and maintainable solution, to strict aliasing rule (don't typecast unless you now that data is really there, just assign), to endianess (take it into account when assigning) and bitfields (I wonder, if compiler can make `sizeof(struct msg)` == 7). Also bitfields should be `int`, `unsigned int` or `_Bool` or implementation defined type. – KamilCuk Sep 03 '18 at 10:13
  • @KamilCuk undefined behaviour on 32-bit int systems. Also please post attempted answers in the answer box, not the comments – M.M Sep 03 '18 at 10:48
  • The two linked duplicates should sum up the problems. – Lundin Sep 03 '18 at 11:09

1 Answers1

2

You can use union to be more portable instead of pointer punning

#include <stdio.h>
#include <stdint.h>


typedef union 
{
    struct 
    {
        uint64_t u56:56;
    };
    uint8_t u8[sizeof(uint64_t)];
}msg;


msg *pune(msg *message, uint8_t *data, size_t datalen, int endianes)
{
    if(endianes)
    {
        for(size_t index = 0; index < datalen; index++)
        {
            message -> u8[index] = data[index];
        }
    }
    else
    {
        for(size_t index = 0; index < datalen; index++)
        {
            message -> u8[index] = data[datalen - index - 1];
        }
    }
    return message;
}


int main()
{
    msg message;
    uint8_t buf[8]={0x7,0x6,0x5,0x4,0x3,0x2,0x1};

    printf("buf=%llx\n",pune(&message, buf, 7, 1) -> u56);
    printf("buf=%llx\n",pune(&message, buf, 7, 0) -> u56);
    return 0;
}
0___________
  • 60,014
  • 4
  • 34
  • 74
  • 1
    The problem with this approach is that the bit-field does not necessarily overlap the array as expected. To begin with, we have no idea where the MSB is allocated. Particularly, this will be problematic on a <=32 bit computer where the compiler might get all kinds of lovely ideas about bit padding etc. The solution is to stay clear of bit-fields entirely and use a plain `uint64_t`. – Lundin Sep 03 '18 at 11:16