除了内存转换技巧之外,还有任何方法可以使用未标记的联合
(显式保存一组不是标记联合的类型之一的数据类型,
即。编译器强制保存一个关联的类型标记,并且可能只允许语言使用以获取正确类型的值)
在容纳它的容器中没有关联的类型标记?
没有标记的联盟对类型联盟有什么其他优势吗?
编辑:显示我在haskell
中标记为union的示例data U = I Int | S String
在c
中手动标记了unionenum u_types {INT,STRING};
typedef struct {
u_types tag;
union u{
int i;
char s[STRING_BUFFER_SIZE];
} d;
}tagged union;
c中未标记的联合
union u{
int i;
char s[STRING_BUFFER_SIZE];
} d;
答案 0 :(得分:6)
无标记联合的一个用途是允许轻松访问较大类型的较小部分:
union reg_a {
uint32_t full;
struct { /* little-endian in this example */
uint16_t low;
uint16_t high;
} __attribute__((__packed__));
};
union reg_a a;
a.full = 0x12345678; /* set all whole 32-bits */
a.high = 0xffff; /* change the upper 16-bits */
union pix_rgba {
uint32_t pix; /* to access the whole 32-bit pixel at once */
struct {
uint8_t red; /* red component only */
uint8_t green; /* green component only */
uint8_t blue; /* blue only */
uint8_t alpha; /* alpha only */
} __attribute__((__packed__));
};
这些用途不一定是完全可移植的,因为它们可能取决于类型,字节序等的具体表示。但是,它们通常足够便携,一个或两个备用版本将涵盖所关注的所有平台,它们非常有用。
当联合存储的内容无论如何都是已知的,即使没有检查标记,并且您不希望存储和更新标记的额外开销,也可以使用未标记的联合。可能在另一个地方的信息,也可能用于其他目的,可能表明联盟中应该包含哪种数据 - 在这种情况下,不需要标记联盟本身。
答案 1 :(得分:1)
如果您只计划定义一个变量,或者在结构中使用它,则不需要标记。
例如:
union
{
int x;
int y;
} u;
void test(void)
{
u.x = 10;
}
如果你打算在多个地方使用它,你需要创建一个指向它的指针等,你只需要标记。
注意:上面的答案假设问题是标准所谓的标签。但是,在给出答案后,问题已更新,表明相关标签是一个额外的类型字段,用于记录联合中哪些字段处于活动状态。
答案 2 :(得分:1)
你发布的“手动标记”是无效的C语法,我想你的意思是:
typedef enum {INT,STRING} u_types;
typedef struct {
u_types tag;
union u{
int i;
char s[1];
} d;
}tagged_union;
请注意,C中struct / union 标记的正式定义是struc / union关键字后面的名称。在第二个示例中,u
是联合标记。这让我很困惑。
您所描述的“标记联合”在计算机科学中被称为变体:一个可以容纳多种类型的变量。在编程中,变体通常是不受欢迎的,特别是在C中。它们在MISRA-C:2004,规则18.3和18.4中被禁止。
支持变体的语言,比如VB(可能还有Haskell?)通常表现为:这个变量可以保存任何东西,但你应该小心使用它,因为效率非常低。
在C中,变体不仅效率低,而且存在安全隐患。 MISRA-c在规则18.3中承认这一点:
例如:当程序实际存储另一种类型的值时(例如由于中断),程序可能会尝试从该位置访问一种类型的数据。这两种类型的数据可能在存储中不同地对齐,并且侵占其他数据。因此,每次使用切换时,可能无法正确初始化数据。在并发系统中,这种做法特别危险。
所以问题应该是,标记的联合(变体)是否有任何用途?不,那里没有。我没有在我编写的任何C程序中使用过单个程序,对它们没用。由于C具有void指针,因此在C中创建通用数据类型有更好,更安全的方法:
void ADT_add_generic_type (void* data, some_enum_t type, size_t size);
看看C标准如何实现函数qsort()和bsearch()以获得通用C编程的一些很好的例子(ISO 9899:1999 7.20.5.1):
void *bsearch (const void *key,
const void *base,
size_t nmemb,
size_t size,
int (*compar)(const void *, const void *));
描述bsearch函数搜索nmemb对象数组, 对于元素,其初始元素由base指向 匹配key指向的对象。每个元素的大小 数组由size指定。
然而,“未标记”的工会的用途有几个。数据协议,打包,硬件寄存器访问等等。请参阅Dmitri的答案以获得一个很好的例子。
答案 3 :(得分:0)
这是一个技巧:
static __attribute__((const, always_inline))
int32_t floatToIntBits(float f) {
union {
float value;
int32_t bits;
};
value = f;
return bits;
}
答案 4 :(得分:0)
无论您使用void *
进行通用实现,都可以使用未标记的联合。由于您使用void *
,因此必须从上下文中了解真实对象类型。
这是实现可以存储union { void *ptr; unsigned x; }
的通用数据结构的最大可移植方式(在没有uintptr_t
的C平台上)。