C 嵌套联合和结构

C Nested Unions and structs

早上好,我正在尝试提出一种可以在不同应用程序中使用的数据结构,但作为相同类型传递给传输函数, 我目前正在使用 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 之后 'value' 现在应该是0xF000。

任何人都可以就我做错了什么以及为什么 'value' 没有读取包含 'lastfour'

的相同内存提供一些指导

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

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

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

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

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

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

要将一些动态类型的数据传递给某些函数,您可能需要一些 tagged union. The Glib GVariant type 是一个真实世界的示例(您可能会深入研究源代码)。

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

中生成的汇编代码

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

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

首先,令我惊讶的是,它甚至可以为您编译。您的 foo 类型中有两个匿名 unions,并且它们具有重复的成员名称(bit4bit5 等)。你的代码没有为我编译。您应该为两个 unions 提供名称或重命名 bits 以免它们冲突。

其次,您的联合 firstfourlastfour 最终可能是 8 位,而不是 4 位,因为 char 的最小大小是 8 位。那会丢掉你所有的其他位。

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

第四,较大的大小是您在测试代码中看到值“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;