Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

C++: Casting unsigned char to a Structure

What I am trying to do

typedef struct {
    unsigned char a;
    unsigned char b;
    unsigned int  c;
} Packet;

unsigned char buffer[] = {1, 1, 0, 0, 0, 1};
Packet pkt = (Packet)buffer;

Basically I am trying to cast a byte array to a structure in C++, when compiling I get:

No matching function call for Packet::Packet(unsigned char[6])

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Is this not possible or do I have to manually index into the array?

>Solution :

There are a few ways to do this:

// packet.h
////////////////
struct Packet {
    unsigned char a;
    unsigned char b;
    unsigned int  c;
};

If you compile and dump the structs with pahole you will see the paddings

$ pahole -dr --structs main.o
struct Packet {
        unsigned char              a;                    /*     0     1 */
        unsigned char              b;                    /*     1     1 */

        /* XXX 2 bytes hole, try to pack */

        unsigned int               c;                    /*     4     4 */

        /* size: 8, cachelines: 1, members: 3 */
        /* sum members: 6, holes: 1, sum holes: 2 */
        /* last cacheline: 8 bytes */
};

So it’s basically the 2 chars, 2 padding bytes and 4 bytes of an int for a total of 8 bytes.

Because Intel is a little endian platform, the least significant byte comes first as in

void print_packet( Packet* pkt ) {
    printf( "a:%d b:%d c:%d\n", int(a), int(b), c );
}
int main() {
    unsigned char buffer[] = {1, 1, 0, 0, 1, 0, 0, 0};
    print_packet( (Packet*) buffer );
    print_packet( reinterpret_cast<Packet*>(buffer));
}

Produces:

$ g++ main.cpp -o main
$ ./main
a:1 b:1 c:1
a:1 b:1 c:1

However one can change the packing from the command line as below where we set the alignment to 2 bytes.

$ g++ -ggdb  main.cpp -o main -fpack-struct=2
$ pahole -dr --structs main
struct Packet {
        unsigned char              a;                    /*     0     1 */
        unsigned char              b;                    /*     1     1 */
        unsigned int               c;                    /*     2     4 */

        /* size: 6, cachelines: 1, members: 3 */
        /* last cacheline: 6 bytes */
} __attribute__((__packed__));

Then you can see that the Packet struct is only 6 bytes and the result of running main is completely different

$ ./main
a:1 b:1 c:65536
a:1 b:1 c:65536

This is because the value of c is now 0x00000100 or 65536

So not to be at mercy of these compiler shenanigans, it is better to define your packet in code with the right packing as

// packet.h
////////////////
struct [[gnu::packed]] Packet {
    unsigned char a;
    unsigned char b;
    unsigned char reserved[2];
    unsigned int  c;
};

Then execution becomes

$ g++ -ggdb  main.cpp x.cpp -o main -fpack-struct=2
$ ./main
a:1 b:1 c:1
a:1 b:1 c:1
$ g++ -ggdb  main.cpp x.cpp -o main -fpack-struct=4
$ ./main
a:1 b:1 c:1
a:1 b:1 c:1
$ g++ -ggdb  main.cpp x.cpp -o main -fpack-struct=8
$ ./main
a:1 b:1 c:1
a:1 b:1 c:1
$ g++ -ggdb  main.cpp x.cpp -o main -fpack-struct=16
$ ./main
a:1 b:1 c:1
a:1 b:1 c:1
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading