早上好,我正在尝试提出一种数据结构,可以在不同的应用程序中使用,但是传入同一类型的传输函数, 我目前正在使用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'的同一记忆
给出一些指导答案 0 :(得分:1)
它是实现定义的(取决于int
- s,处理器,endianness,ABI等的大小...)。在具有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 union。 Glib GVariant type是一个真实的例子(您可能会深入了解源代码)。
如果你知道一些汇编程序,你可以尝试查看编译器生成的汇编程序代码。如果使用GCC进行编译,请尝试使用gcc -Wall -fverbose-asm -O -S your-main.c
编译您的程序,然后(使用编辑器或寻呼机)查看your-main.s
请注意(假设您不使用已成为过时的register
关键字)每个数据变量或聚合字段或数组组件都是可寻址的(您可以使用地址 - 一元前缀&
运算符)并且实际上可以作为连续字节存在于内存中,具有一些对齐约束。但是,位域(和register
变量)是一个例外。它们不可寻址,并且位域通常位于内部某些可寻址存储区中,具体实现方式。
一般经验法则是避免位域。该规则有例外,但您应该首先了解更多有关C的信息。
答案 1 :(得分:1)
首先,我很惊讶这甚至可以为你编译。您的unions
类型中有两个匿名foo
,他们的成员名称相同(bit4
,bit5
等)。你的代码没有为我编译。您应该提供两个unions
的名称或重命名bits
,以便它们不会发生冲突。
其次,您的工会firstfour
和lastfour
可能最终会成为8位,而不是4位,因为char
的最小大小为8位。那会甩掉你所有的其他部分。
第三,您的工会firstfour
和lastfour
不会从内存中的第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;