Union and struct packing problem

风格不统一 提交于 2019-11-29 20:13:10

问题


I'm writing some software where each bit must be exact(it's for the CPU) so __packed is very important.

typedef union{
uint32_t raw;
struct{
    unsigned int present:1;
    unsigned int rw:1;
    unsigned int user:1;
    unsigned int dirty:1;
    unsigned int free:7;
    unsigned int frame:20;
} __packed;
}__packed page_union_t;

that is my structure and union. It does not work however:

page_union_t p; //.....
//This:
p.frame=trg_page;
p.user=user;
p.rw=rw;
p.present=present;
//and this:
p.raw=trg_page<<12 | user<<2 | rw<<1 | present;

should create the same uint32. But they do not create the same thing.

Is there something I can not see that is wrong with my union?


回答1:


Your struct has only 31 bits




回答2:


AFAIK, the order in which the bits in the struct are stored is undefined by the C99 standard (and the C89 standard too). Most likely, the bits are in the reverse order from what you expected.

You should have shown the result you got as well as the result you expected - it would help us with the diagnosis. The compiler you use and the platform you run on could also be significant.


On MacOS X 10.4.11 (PowerPC G4), this code:

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

typedef union
{
        uint32_t raw;
        struct
        {
                unsigned int present:1;
                unsigned int rw:1;
                unsigned int user:1;
                unsigned int dirty:1;
                unsigned int free:7;
                unsigned int frame:20;
        };
} page_union_t;

int main(void)
{
        page_union_t p = { .raw = 0 }; //.....
        unsigned trg_page = 0xA5A5A;
        unsigned user = 1;
        unsigned rw = 1;
        unsigned present = 1;

        p.frame = trg_page;
        p.user = user;
        p.rw = rw;
        p.present = present;

        printf("p.raw = 0x%08X\n", p.raw);

        p.raw = trg_page<<12 | user<<2 | rw<<1 | present;
        printf("p.raw = 0x%08X\n", p.raw);

        p.raw <<= 1;
        printf("p.raw = 0x%08X\n", p.raw);
        return(0);
}

produces the results shown:

p.raw = 0xE014B4B4
p.raw = 0xA5A5A007
p.raw = 0x4B4B400E

With the order of the fields reversed, the result is more nearly explicable:

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

typedef union
{
        uint32_t raw;
        struct
        {
                unsigned int frame:20;
                unsigned int free:7;
                unsigned int dirty:1;
                unsigned int user:1;
                unsigned int rw:1;
                unsigned int present:1;
        };
} page_union_t;

int main(void)
{
        page_union_t p = { .raw = 0 }; //.....
        unsigned trg_page = 0xA5A5A;
        unsigned user = 1;
        unsigned rw = 1;
        unsigned present = 1;

        p.frame = trg_page;
        p.user = user;
        p.rw = rw;
        p.present = present;

        printf("p.raw = 0x%08X\n", p.raw);

        p.raw = trg_page<<12 | user<<2 | rw<<1 | present;
        printf("p.raw = 0x%08X\n", p.raw);

        p.raw <<= 1;
        printf("p.raw = 0x%08X\n", p.raw);
        return(0);
}

This gives the result:

p.raw = 0xA5A5A00E
p.raw = 0xA5A5A007
p.raw = 0x4B4B400E

The first result has an E as the last hex digit because the least significant bit is not used, because the bit-field structure has only 31-bits defined..




回答3:


If the exact position of bits matters, your safest bet is explicit packing and unpacking of the structure into an unsigned char array. Anything else is too implementation dependent.




回答4:


For reference to anyone who might find this, try the packed attribute:

struct __attribute__((packed)){

}



回答5:


You don't mention that you are clearing out the bits of the structure beforehand, are you sure you aren't ending up with garbage bits left over in the first case?

// maybe try this
page_union_t p = {0};


来源:https://stackoverflow.com/questions/979050/union-and-struct-packing-problem

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!