10

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.

Irina
  • 149
  • 1
  • 1
  • 12
  • 4
    You may want a `union` rather than a `struct` here. Else you can only set each variable in the struct separately. you can not do `CPUID CPUIDoutput = EAX;` you'll have to do `CPUIDoutput.stepping = EAX;` – andre Jun 01 '12 at 13:46

4 Answers4

14

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;
}
Shahbaz
  • 44,690
  • 18
  • 114
  • 177
10

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;
}
Ben
  • 101
  • 1
  • 2
  • Nice one, would there also be something to avoid spelling out a.allfields? So for example just a = 42. Even if I have to use a GCC extension to accomplish that. – Zeyneb Nov 15 '21 at 02:16
2

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

*(reinterpret_cast<uint32_t *> (&CPUIDoutput)) = CPUIDregsoutput.EAXBuf;
Irina
  • 149
  • 1
  • 1
  • 12
  • 3
    @Anna, I hope you are not using this in real code that would be used in real situations. The problem is that the compiler is free to add padding between members of your struct for optimization purposes. So, you cannot rely on your struct and your number to have the same layout. Even without padding, your statement will absolutely be wrong in a big-endian machine. – Shahbaz Dec 09 '12 at 09:51
1

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.

unwind
  • 378,987
  • 63
  • 458
  • 590
  • I guess in edited code version it is more obviously why I am trying to assign EAX to the whole structure at once. So, anyway if I understood you right, it is no way for me to do it? I can only assign each field separately? – Irina Jun 01 '12 at 14:29