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

发布于 2024-12-20 03:55:24 字数 561 浏览 2 评论 0原文

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

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

即编译器强制保存关联的类型标记的数据类型)并且可能只允许语言获取正确类型的值),

而在保存它的容器中没有关联的类型标记?

与类型化联合相比,无标签联合还有其他优势吗?

编辑:显示我的意思 haskell 中的示例标记联合

data U = I Int | S String

在 c 中手动标记联合

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;

Other then memory casting tricks is there any way to use an untagged union

(a data type that explicitly hold one of a set of types that isn't a tagged union,

ie. one that is forced by the compiler to hold an associated type tag and possibly only allowed by the language to get the value of the proper type)

without an associated type tag in the container that holds it?

Are there any other advantage an untagged union holds over a typed union?

edit: to show what I mean example tagged union in haskell

data U = I Int | S String

manually tagged union in c

enum u_types {INT,STRING};
typedef struct {
    u_types tag;
    union u{
    int i;
    char s[STRING_BUFFER_SIZE];
    } d;
}tagged union;

untagged union in c

    union u{
    int i;
    char s[STRING_BUFFER_SIZE];
    } d;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

携君以终年 2024-12-27 03:55:24

无标记联合的一种用途是允许轻松访问较大类型的较小部分:

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__));
};

但这些用途不一定完全可移植,因为它们可能取决于类型、字节顺序等的特定表示。不过,它们通常是可移植的足以让一两个替代版本涵盖人们关心的所有平台,并且它们可能非常有用。

当联合体中存储的内容无论如何都已知(即使不检查标签)并且您不希望存储和更新标签的额外开销时,未标记的联合体也很有用。可能位于另一个地方的信息也可能用于另一个目的,可能表明联合中应该包含什么类型的数据——在这种情况下,不需要标记联合本身。

One use for untagged unions is to allow easy access to smaller parts of a larger type:

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__));
};

These sorts of uses are not necessarily completely portable though, since they may depend on specific representations of types, endianness, etc. Still, they're often portable enough that one or two alternate versions will cover all the platforms one cares about, and they can be quite useful.

Untagged unions are also useful when what is stored in the union will be known anyway, even without checking the tag, and you don't want the extra overhead of storing and updating a tag. Possibly information in another place, that may also serve another purpose, may indicate what kind of data should be in the union -- in which case there's no need tag the union itself.

回首观望 2024-12-27 03:55:24

如果您只打算定义一个变量或在结构体中使用它,则不需要该标记。

例如:

union
{
  int x;
  int y;
} u;

void test(void)
{
  u.x = 10;
}

如果您打算在多个地方使用它,如果您需要创建一个指向它的指针等,那么您只需要该标签。

注意:上面的答案假设问题是关于什么的该标准称为标签。然而,给出答案后,问题被更新以表明有问题的标签是一个额外类型字段,用于记录联合中的哪些字段处于活动状态。

You don't need the tag if you only plan to define one variable, or use it inside a struct.

For example:

union
{
  int x;
  int y;
} u;

void test(void)
{
  u.x = 10;
}

You only need the tag if you plan to use it in more than one place, if you need to create a pointer to it etc.

Note: The answer above assumed that the question was about what the standard calls a tag. However, after the answer was given, the question was updated to indicate that the tag in question was an extra type field used to record which of the fields in the union was active.

明月夜 2024-12-27 03:55:24

您发布的“手动标记”不是有效的 C 语法,我想您的意思是:

typedef enum {INT,STRING} u_types;

typedef struct {
    u_types tag;
    union u{
    int i;
    char s[1];
    } d;
}tagged_union;

请注意,C 中结构/联合标记的正式定义是结构之后的名称/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 指向的对象匹配。每个元素的大小
数组由大小指定。


然而,“未标记”联合的用途有多种。数据协议、打包、硬件寄存器访问等。请参阅 Dmitri 的答案以获得一个很好的例子。

What you have posted as "manually tagged" is not valid C syntax , I suppose you meant it to be:

typedef enum {INT,STRING} u_types;

typedef struct {
    u_types tag;
    union u{
    int i;
    char s[1];
    } d;
}tagged_union;

Please note that the formal definition of a struct/union tag in C is the name after the struc/union keyword. In your second example, u is a union tag. This is what had me quite confused.


What you describe as "tagged union" is known as a variant in computer science: a variable which can hold multiple types. Variants are generally frowned upon in programming in general and in C in particular. They are banned in MISRA-C:2004, rules 18.3 and 18.4.

Languages with support for variants, like VB (and probably Haskell?) typically present then as: this variable can hold anything, but you should be careful with using it, because it is very inefficient.

In C, variants are not only inefficient, they are a safety hazard. MISRA-c recognizes this in rule 18.3:

For example: a program might try to access data of one type from the location when actually it is storing a value of the other type (e.g. due to an interrupt). The two types of data may align differently in the storage, and encroach upon other data. Therefore the data may not be correctly initialised every time the usage switches. The practice is particularly dangerous in concurrent systems.


So the question should rather be, are there any uses for tagged unions (variants)? No, there is not. I haven't used a single one in any C program I have ever written, there is no use for them. Since C has void pointers, there are far better and safer ways in C to create generic data types:

void ADT_add_generic_type (void* data, some_enum_t type, size_t size);

Take a look at how the C standard implements the functions qsort() and bsearch() for some good examples of generic C programming (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 *));

Description The bsearch function searches an array of nmemb objects,
the initial element of which is pointed to by base, for an element
that matches the object pointed to by key. The size of each element of
the array is specified by size.


The uses for "untagged" unions are several, however. Data protocols, packing, hardware register access etc etc. See Dmitri's answer for a good example.

风向决定发型 2024-12-27 03:55:24

这是一个技巧:

static __attribute__((const, always_inline))
int32_t floatToIntBits(float f) {
    union {
        float value;
        int32_t bits;
    };
    value = f;
    return bits;
}

Here's a trick:

static __attribute__((const, always_inline))
int32_t floatToIntBits(float f) {
    union {
        float value;
        int32_t bits;
    };
    value = f;
    return bits;
}
蓝眼泪 2024-12-27 03:55:24

无论您在何处使用 void * 进行通用实现,都可以使用未标记的联合。由于您使用的是 void *,因此必须从上下文中了解真实的对象类型。

这是实现可以存储 union { void *ptr; 的通用数据结构的最大可移植方式。无符号 x;例如(在没有 uintptr_t 的 C 平台上)。

Wherever you have a generic implementation using void *, you could use an untagged union instead. Since you are using void * the true object type has to be known from context.

This is a maximally portable way to implement a generic datastructure that can store union { void *ptr; unsigned x; } for example (on C platforms where there is no uintptr_t).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文