对于无类型联合(没有类型标签)有什么用途吗?

时间:2011-12-09 09:02:04

标签: c unions algebraic-data-types

除了内存转换技巧之外,还有任何方法可以使用未标记的联合

(显式保存一组不是标记联合的类型之一的数据类型,

即。编译器强制保存一个关联的类型标记,并且可能只允许语言使用以获取正确类型的值)

在容纳它的容器中没有关联的类型标记?

没有标记的联盟对类型联盟有什么其他优势吗?

编辑:显示我在haskell

中标记为union的示例
data U = I Int | S String

在c

中手动标记了union
enum 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;

5 个答案:

答案 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平台上)。

相关问题