简体   繁体   中英

C Nested Unions and structs

Good Morning, I am trying to come up with a data structure which can be used in different applications, yet passed in to a transmit function as the same type, I am using netbeans at the moment but this will be transferred on to a dspic30f (16-bit),

typedef union {

    union {

        struct {
            unsigned bit0 : 1;
            unsigned bit1 : 1;
            unsigned bit2 : 1;
            unsigned bit3 : 1;
            unsigned bit4 : 1;
            unsigned bit5 : 1;
            unsigned bit6 : 1;
            unsigned bit7 : 1;

            unsigned bit8 : 1;
            unsigned bit9 : 1;
            unsigned bit10 : 1;
            unsigned bit11 : 1;

            union {

                struct {
                    unsigned bit12 : 1;
                    unsigned bit13 : 1;
                    unsigned bit14 : 1;
                    unsigned bit15 : 1;
                };
                unsigned char value;
            } lastfour;

        };
        unsigned int value : 16;
    };

    union {

        struct {

            union {

                struct {
                    unsigned bit0 : 1;
                    unsigned bit1 : 1;
                    unsigned bit2 : 1;
                    unsigned bit3 : 1;
                };
                unsigned char value;
            } firstfour;

            unsigned bit4 : 1;
            unsigned bit5 : 1;
            unsigned bit6 : 1;
            unsigned bit7 : 1;

            unsigned bit8 : 1;
            unsigned bit9 : 1;
            unsigned bit10 : 1;
            unsigned bit11 : 1;
            unsigned bit12 : 1;
            unsigned bit13 : 1;
            unsigned bit14 : 1;
            unsigned bit15 : 1;


        };
        unsigned int value : 16;
    };

} foo;

I then use the following code to check the functionality.

int main(int argc, char** argv) {

     foo a;
     a.value =0;
     a.lastfour.value = 0xF;

     printf("%d", a.value);

     return (EXIT_SUCCESS);
 }

The printed value is 0, however because of the union I am under the impression the two structure share the same memory (16 bits) so after setting 'lastfour' to 0xF 'value' should now be 0xF000.

Could anyone give some guidance on what I am doing wrong and why 'value' is not reading the same memory which contains 'lastfour'

It is implementation defined (depends upon the size of int -s, the processor, the endianness , the ABI , etc...). It certainly would be different on an Android tablet with an ARM processor and on an x86-64 desktop running some 64 bits flavor of Linux (distribution).

I believe you should avoid union -s with bitfields in struct unless you are thinking of a particular implementation.

I am not sure that your code enables you to call arbitrary function (in particular, because pointers might have different sizes than int -s; you might want to use intptr_t ), but this has not much in common with your code.

If you want to be able to call an arbitrary function of arbitrary signature consider using some library like libFFI (which is of course implementation specific).

Notice that bitfields are implementation specific and are not very efficient (in terms of access time). For software running on desktops or laptops they are almost always useless. They are more useful in implementation specific low-level embedded code (eg the microcontroller inside your washing machine), and then you should know what your implementation (including your compiler) is precisely doing.

BTW, your code is wrong since lastfour contains a char (generally an 8 bits byte) so it cannot take the same place as a 4-bits bitfield ( bits12 ... bits15 ); maybe you should replace unsigned char value; in your firstfour with unsigned valfourbits : 4; etc...

To pass some dynamically typed data to some function, you might want to have some tagged union . The Glib GVariant type is a real-world example (and you might dive into the source code).

If you know a bit of assembler, you could try to look into the assembler code generated by your compiler. If compiling with GCC , try to compile your program using gcc -Wall -fverbose-asm -O -S your-main.c then look (with an editor or pager) into the generated assembler code in your-main.s

Notice that (assuming you don't use the register keyword, which has become obsolete) every data -variable or aggregate field or array component- is addressable (you can use the address-of unary prefix & operator) and in practice may sit in memory, as consecutive bytes, with some alignment constraint. However, bitfields (and register variables) are an exception. They are not addressable, and bitfields usually sit inside some addressable memory zone, in a implementation-specific way.

The general rule of thumb is to avoid bitfields . That rule has exceptions, but you should first learn a lot more about C.

First, I'm surprised this even compiles for you. You have two anonymous unions in your foo type, and they have duplicate member names ( bit4 , bit5 , etc.). Your code didn't compile for me. You should provide names for the two unions or rename the bits so they don't conflict.

Second, your unions firstfour and lastfour will likely end up being 8 bits, not 4, since the minimum size of a char is 8 bits. That's going to throw off all your other bits.

Third, your unions firstfour and lastfour will not start at bit 12 in memory. They will be aligned as necessary for your processor, likely at the next 2-byte or 4-byte offset. Try printing sizeof(foo) in your function. I guarantee you'll see something like 4 or 8, not 2 like you're expecting.

Fourth, that larger size is why you're seeing the value "0" printed in your test code. The first 16 bits are all zero. The 0xF you set is either in the next 16 bits or possibly in the next 32 bits, depending how your compiler aligned things.

Here is a structure layout that should work for what you're trying to do. I tested it and it works for me. Packs everything into 2 bytes.

typedef struct {
    union {
        struct {
            uint16_t firstfour  : 4;
            uint16_t secondfour : 4;
            uint16_t thirdfour  : 4;
            uint16_t lastfour   : 4;
        };
        /* EDIT - Duplicate structure with different member names
           added, in response to a comment below. */
        struct {
            uint16_t nibble1    : 4;
            uint16_t nibble2    : 4;
            uint16_t nibble3    : 4;
            uint16_t nibble4    : 4;
        };
        struct {
            uint16_t bit0  : 1;
            uint16_t bit1  : 1;
            uint16_t bit2  : 1;
            uint16_t bit3  : 1;
            uint16_t bit4  : 1;
            uint16_t bit5  : 1;
            uint16_t bit6  : 1;
            uint16_t bit7  : 1;
            uint16_t bit8  : 1;
            uint16_t bit9  : 1;
            uint16_t bit10 : 1;
            uint16_t bit11 : 1;
            uint16_t bit12 : 1;
            uint16_t bit13 : 1;
            uint16_t bit14 : 1;
            uint16_t bit15 : 1;
        };
        uint16_t value;
    };
} foo;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM