匿名结构和联合何时在 C11 中有用?

Posted

技术标签:

【中文标题】匿名结构和联合何时在 C11 中有用?【英文标题】:When are anonymous structs and unions useful in C11? 【发布时间】:2012-02-14 12:06:54 【问题描述】:

C11 添加了“匿名结构和联合”等内容。

我四处寻找,但找不到关于匿名结构和联合何时有用的明确解释。我问是因为我不完全理解它们是什么。我知道它们是后来没有名称的结构或联合,但我一直(不得不?)将其视为错误,因此我只能设想使用命名结构。

【问题讨论】:

How to use anonymous structs / unions in c? 的可能重复项 @wallyk 不是同一个问题。 【参考方案1】:

结构内部的匿名联合在实践中非常有用。考虑到您想要实现一个可区分的和类型(或tagged union),一个带有布尔值和浮点数或char*(即字符串)的聚合,具体取决于布尔标志。使用 C11 你应该能够编码

typedef struct 
    bool is_float;
    union 
       float f;
       char* s;
    ;
 mychoice_t;

double as_float(mychoice_t* ch) 
 
   if (ch->is_float) return ch->f;
   else return atof(ch->s);

使用 C99,您必须命名联合,并使用代码 ch->u.fch->u.s,这样可读性较差且更冗长。

另一种实现tagged union 类型的方法是使用强制转换。 Ocaml runtime 举了很多例子。

Common Lisp 的SBCL 实现确实使用了一些union 来实现tagged union 类型。而GNU make 也使用它们。

【讨论】:

啊,等等,现在的问题是 C11 添加了对“结构/联合内部的匿名结构和联合”的支持吗? 至少这是我能很快想象到的最有用的情况。事实上,GCC 很久以前就支持它作为扩展,我一直为此祝福它...... 谢谢。这是有道理的,我现在至少得到了一个应用程序。 注意 bool 在 C 中不是默认类型,bool 仅在 C++ 中有效。 @Renato in c99 提供布尔值。【参考方案2】:

匿名结构和联合的典型和现实使用是为数据提供另一种视图。例如在实现 3D 点类型时:

typedef struct 
    union
        struct
            double x; 
            double y;
            double z;
        ;
        double raw[3];
    ;
vec3d_t;

vec3d_t v;
v.x = 4.0;
v.raw[1] = 3.0; // Equivalent to v.y = 3.0
v.z = 2.0;

如果您接口的代码需要一个 3D 向量作为指向三个双精度的指针,这很有用。与其使用丑陋的f(&v.x),不如使用f(v.raw),这会使您的意图更加明确。

【讨论】:

...可能是成员类型,但不是取其访问的成员地址形成的。鉴于编译器正在朝着更积极而不是更理智的方向发展,并且愿意使用对标准的极度紧张的解释来证明他们的行为是正当的,我不相信编译器在没有-fno-strict-aliasing 的情况下可以有效地处理上述代码。 当这是一个 C 问题时,你为什么要引用 C++? 这只是回避了这个问题。为什么有人会在关于 C 的问题中引用 C++ 标准?想要理解这一点的人必须去查阅正确的标准,以确保他们同意这一点。或者你可以翻身说,“好吧,如果它在 C++ 中是真的,那么它在 C 中一定是真的......” @davidbowling 我在答案写完两年后回答评论时忘记了它是 C,请原谅我是人类。我没有时间或动力去寻找合适的报价,欢迎您改进答案或提供相关报价或反报价。 为什么需要外部结构?为什么不能将 union 直接键入vec3d_t【参考方案3】:
struct bla 
    struct  int a; int b; ;
    int c;
;

struct bla 类型有一个 C11 匿名结构类型的成员。

struct int a; int b; 没有标签,对象没有名字:它是一个匿名结构类型。

您可以通过这种方式访问​​匿名结构的成员:

struct bla myobject;
myobject.a = 1;  // a is a member of the anonymous structure inside struct bla   
myobject.b = 2;  // same for b
myobject.c = 3;  // c is a member of the structure struct bla

【讨论】:

而只做struct bla int a;intb;intc;;有什么区别? @Zaibis 访问结构的成员没有区别,但具有匿名结构的版本包含一个额外的信息:ab 之间存在一些逻辑关系存在于c 您能解释一下这些信息有什么用处吗?这是表演的事情吗?或者它是关于什么的? @Zaibis 以 API 为例,此信息可能对读者有用,因为它公开了有关 ab 本质的不同信息。 @Zaibis - 内部结构可以自己命名和使用。一种用例是实现继承(外部结构扩展内部结构)。【参考方案4】:

另一个有用的实现是在处理 rgba 颜色时,因为您可能希望单独访问每种颜色或作为单个 int 访问。

typedef struct 
    union
        struct uint8_t a, b, g, r;;
        uint32_t val;
    ;
Color;

现在您可以访问单个 rgba 值或整个值,其最高字节为 r。即:

int main(void)

    Color x;
    x.r = 0x11;
    x.g = 0xAA;
    x.b = 0xCC;
    x.a = 0xFF;

    printf("%X\n", x.val);

    return 0;

打印 11AACCFF

【讨论】:

也许你只是表明你可以做到这一点,但你为什么要使用外部结构?如果您删除外部结构并 typedef 联合,您的代码似乎表现相同。【参考方案5】:

我不确定为什么 C11 允许在结构中使用匿名结构。但是Linux用a certain language extension来使用它:

/**
 * struct blk_mq_ctx - State for a software queue facing the submitting CPUs
 */
struct blk_mq_ctx 
    struct 
        spinlock_t      lock;
        struct list_head    rq_lists[HCTX_MAX_TYPES];
     ____cacheline_aligned_in_smp;

    /* ... other fields without explicit alignment annotations ... */

 ____cacheline_aligned_in_smp;

我不确定该示例是否绝对必要,除非是为了明确意图。

编辑:我发现了另一种更清晰的类似模式。匿名结构特性与此属性一起使用:

#if defined(RANDSTRUCT_PLUGIN) && !defined(__CHECKER__)
#define __randomize_layout __attribute__((randomize_layout))
#define __no_randomize_layout __attribute__((no_randomize_layout))
/* This anon struct can add padding, so only enable it under randstruct. */
#define randomized_struct_fields_start  struct 
#define randomized_struct_fields_end     __randomize_layout;
#endif

即用于随机化字段顺序的语言扩展/编译器插件(ASLR 风格的利用“强化”):

struct kiocb 
    struct file     *ki_filp;

    /* The 'ki_filp' pointer is shared in a union for aio */
    randomized_struct_fields_start

    loff_t          ki_pos;
    void (*ki_complete)(struct kiocb *iocb, long ret, long ret2);
    void            *private;
    int         ki_flags;
    u16         ki_hint;
    u16         ki_ioprio; /* See linux/ioprio.h */
    unsigned int        ki_cookie; /* for ->iopoll */

    randomized_struct_fields_end
;

【讨论】:

【参考方案6】:

好吧,如果您只在代码中声明该结构中的变量一次,为什么它需要一个名称?

struct 
 int a;
 struct 
  int b;
  int c;
  d;
 e,f;

你现在可以写e.af.d.b等内容了。

(我添加了内部结构,因为我认为这是匿名结构的最多用法之一)

【讨论】:

这是正确的,提交的时间比我接受的要早一点。抱歉,那个人“解释”得好一点,但现在我明白了,我认为这是一个很好的答案。 这不是正在讨论的功能,并且此代码没有使用 C11 的任何新功能。示例中的结构是not 匿名的:它们的名称分别为.def。他们有匿名类型,但那是不同的。 匿名结构没有标识符和标签。

以上是关于匿名结构和联合何时在 C11 中有用?的主要内容,如果未能解决你的问题,请参考以下文章

C语言中的匿名结构体

C语言 匿名联合体和匿名结构体

是否通过 C99 中未指定的联合进行类型双关,并且它是否已在 C11 中指定?

是否通过 C99 中未指定的联合进行类型双关,并且它是否已在 C11 中指定?

如何使用匿名结构/联合编译 C 代码?

C++匿名联合重新声明错误