Irina
Irina

Reputation: 179

How to assign value to a struct with bit-fields?

I have a struct with bit-fields (totally 32 bit width) and I have a 32-bit variable. When I try to assign the variable value to my struct, I got an error:

error: conversion from ‘uint32_t {aka unsigned int}’ to non-scalar type ‘main()::CPUID’ requested.

struct CPUIDregs
    {
       uint32_t EAXBuf;
    };
CPUIDregs CPUIDregsoutput;   


int main () {

 struct CPUID          
    {
          uint32_t   Stepping         : 4;         
          uint32_t   Model            : 4;        
          uint32_t   FamilyID         : 4;        
          uint32_t   Type             : 2;        
          uint32_t   Reserved1        : 2;         
          uint32_t   ExtendedModel    : 4;         
          uint32_t   ExtendedFamilyID : 8;          
          uint32_t   Reserved2        : 4;          
    };

    CPUID CPUIDoutput = CPUIDregsoutput.EAXBuf;

Do you have any idea how to do it in the shortest way? Thanks

P.S. Of course I have more appropriate value of EAX in real code, but I guess it doesn't affect here.

Upvotes: 13

Views: 22522

Answers (5)

Wulf
Wulf

Reputation: 1

I wanted to add a couple of things here in case someone needs the information. As Shahbaz has pointed out, there is one way of SAFELY doing this. Here are the issues with the other ones I see.

The union has the problem of endian-ness. When you have a uint32_t value assigned to a bitfield, big endian will have your bits in one order while little endian will store your bytes of bits in reverse order. When you think you are assigning it one value, you could instead be assigning wrong values to wrong bits. This is therfore NOT portable code.

When you change the pointer type of anything on the LHS of the assignment, assume you are doing something wrong. This is my mantra. Sure, it works, here. Again, this becomes dependent on endian-ness, as well as compiler settings and memory allocation. I can remap any chunk of memory into a different structure, but if I'm doing that then how do I ensure that the 2 structures are of the same size and order? What if down the road you decide to optimize your code and make one a uint16_t, or reorder the bits on a structure? This is a maintenance NIGHTMARE. Just... don't do it. If not for yourself, then for the next individual who is going to have to maintain your code.

Hope this helps explain some of the answers that get the job done but aren't the way to do it.

Upvotes: 0

Shahbaz
Shahbaz

Reputation: 47523

You should never rely on how the compiler lays out your structure in memory. There are ways to do what you want with a single assignment, but I will neither recommend nor tell you.

The best way to do the assignment would be the following:

static inline void to_id(struct CPUid *id, uint32_t value)
{
    id->Stepping         = value & 0xf;
    id->Model            = value >> 4 & 0xf;
    id->FamilyID         = value >> 8 & 0xf;
    id->Type             = value >> 12 & 0x3;
    id->Reserved1        = value >> 14 & 0x3;
    id->ExtendedModel    = value >> 16 & 0xf;
    id->ExtendedFamilyID = value >> 20 & 0xff;
    id->Reserved2        = value >> 28 & 0xf;
}

And the opposite

static inline uint32_t from_id(struct CPUid *id)
{
    return id->Stepping
         | id->Model << 4
         | id->FamilyID << 8
         | id->Type << 12
         | id->Reserved1 << 14
         | id->ExtendedModel << 16
         | id->ExtendedFamilyID << 20
         | id->Reserved2 << 28;
}

Upvotes: 20

Ben
Ben

Reputation: 121

Use a union.

union foo {
    struct {
        uint8_t a : 4;
        uint8_t b : 4;
        uint8_t c : 4;
        uint8_t d : 4;
        uint16_t e;
    };
    uint32_t allfields;
};

int main(void) {
    union foo a;

    a.allfields = 0;
    a.b = 3;

    return 0;
}

Upvotes: 12

Irina
Irina

Reputation: 179

Just if somebody´s interested, I´ve got a better solution for my own question:

*(reinterpret_cast<uint32_t *> (&CPUIDoutput)) = CPUIDregsoutput.EAXBuf;

Upvotes: 2

unwind
unwind

Reputation: 399833

These are struct members, so you need to assign directly do them, or make sure the RHS of your assignment is a value of type CPUID. Not sure why you expect to be able to assign to the struct from an integer.

The facts that the struct contains bitfields, and that the sum of the bits happens to be the same as the number of bits in the integer you're trying to assign, mean nothing. They're still not compatible types, for assignment purposes.

If this was too vague, consider showing more/better code.

Upvotes: 1

Related Questions