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
,并且它们具有重复的成员名称(bit4
、bit5
等)。你的代码没有为我编译。您应该为两个 unions
提供名称或重命名 bits
以免它们冲突。
其次,您的联合 firstfour
和 lastfour
最终可能是 8 位,而不是 4 位,因为 char
的最小大小是 8 位。那会丢掉你所有的其他位。
第三,联合 firstfour
和 lastfour
不会从内存中的第 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;
早上好,我正在尝试提出一种可以在不同应用程序中使用的数据结构,但作为相同类型传递给传输函数, 我目前正在使用 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
,并且它们具有重复的成员名称(bit4
、bit5
等)。你的代码没有为我编译。您应该为两个 unions
提供名称或重命名 bits
以免它们冲突。
其次,您的联合 firstfour
和 lastfour
最终可能是 8 位,而不是 4 位,因为 char
的最小大小是 8 位。那会丢掉你所有的其他位。
第三,联合 firstfour
和 lastfour
不会从内存中的第 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;