C嵌套联合和结构

时间:2015-03-20 11:34:29

标签: c struct unions

早上好,我正在尝试提出一种数据结构,可以在不同的应用程序中使用,但是传入同一类型的传输函数, 我目前正在使用netbeans,但这将被转移到dspic30f(16位),

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;

然后我使用以下代码检查功能。

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

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

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

     return (EXIT_SUCCESS);
 }

打印值为0,但是由于联合我在印象中两个结构共享相同的内存(16位)所以在将'lastfour'设置为0xF'值'后现在应该是0xF000。

任何人都可以就我做错了什么以及为什么'价值'没有读取包含'lastfour'的同一记忆

给出一些指导

2 个答案:

答案 0 :(得分:1)

它是实现定义的(取决于int - s,处理器,endiannessABI等的大小...)。在具有ARM处理器的Android平板电脑和运行64位Linux(分发版)的x86-64桌面上肯定会有所不同。

我相信你应该避免使用union中的struct - s,除非你正在考虑特定的实现。

我不确定你的代码是否允许你调用任意函数(特别是因为指针的大小可能不同于int - s;你可能想要使用intptr_t),但这有与你的代码没什么共同之处。

如果你想能够调用任意签名的任意函数,可以考虑使用像libFFI这样的库(这当然是特定于实现的)。

请注意, 位域是特定于实现的,并且效率不高(就访问时间而言)。对于在台式机或笔记本电脑上运行的软件,它们几乎总是无用的。它们在实现特定的低级嵌入代码(例如洗衣机内的microcontroller)中更有用,然后您应知道您的实施(包括你的编译器正在做。

顺便说一下,你的代码是错误的,因为lastfour包含char(通常是8位字节)所以它不能占用与4位位域(bits12相同的位置。 。bits15);也许您应该用unsigned char value;等替换firstfour中的unsigned valfourbits : 4; ...

要将某些动态类型的数据传递给某个函数,您可能需要一些tagged unionGlib GVariant type是一个真实的例子(您可能会深入了解源代码)。

如果你知道一些汇编程序,你可以尝试查看编译器生成的汇编程序代码。如果使用GCC进行编译,请尝试使用gcc -Wall -fverbose-asm -O -S your-main.c编译您的程序,然后(使用编辑器或寻呼机)查看your-main.s

中生成的汇编程序代码

请注意(假设您不使用已成为过时的register关键字)每个数据变量或聚合字段或数组组件都是可寻址的(您可以使用地址 - 一元前缀&运算符)并且实际上可以作为连续字节存在于内存中,具有一些对齐约束。但是,位域(和register变量)是一个例外。它们不可寻址,并且位域通常位于内部某些可寻址存储区中,具体实现方式。

一般经验法则避免位域。该规则有例外,但您应该首先了解更多有关C的信息。

答案 1 :(得分:1)

首先,我很惊讶这甚至可以为你编译。您的unions类型中有两个匿名foo,他们的成员名称相同(bit4bit5等)。你的代码没有为我编译。您应该提供两个unions的名称或重命名bits,以便它们不会发生冲突。

其次,您的工会firstfourlastfour可能最终会成为8位,而不是4位,因为char的最小大小为8位。那会甩掉你所有的其他部分。

第三,您的工会firstfourlastfour不会从内存中的第12位开始。它们将根据处理器的需要进行对齐,可能在下一个2字节或4字节偏移处。尝试在您的函数中打印sizeof(foo)。我保证你会看到像4或8这样的东西,而不是像你期望的那样。

第四,更大的尺寸是您在测试代码中看到值“0”的原因。前16位全为零。您设置的0xF可以是接下来的16位,也可能是接下来的32位,具体取决于编译器的对齐方式。

这是一个结构布局,应该适用于您要做的事情。我测试了它,它对我有用。将所有内容打包成2个字节。

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;