什么是在C11中有用的匿名结构和联合?

gri*_*eak 46 c struct c11

除其他事项外,C11还增加了"匿名结构和联盟".

我四处寻找,但无法找到匿名结构和工会何时有用的明确解释.我问,因为我不完全明白它们是什么.我得知它们之后是没有名字的结构或联合,但我总是(必须?)将其视为一个错误,所以我只能设想一个用于命名结构的用法.

Bas*_*tch 52

内部结构中的匿名联合在实践中非常有用.考虑您要实现一个有区别的和类型(或标记的联合),一个带有布尔值的聚合以及一个浮点数或一个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);
}
Run Code Online (Sandbox Code Playgroud)

使用C99,您必须为联合命名,代码ch->u.fch->u.s 可读性更低且更详细.

  • c99 <stdbool.h>中的@Renato提供bool. (11认同)
  • 至少这是我可以快速想象的最有用的情况。事实上,GCC 很久以前就支持它作为扩展,我一直为此祝福它...... (3认同)
  • 啊,等等,C11 增加了对“结构/联合内部的匿名结构和联合”的支持的问题是什么? (2认同)

Emi*_* L. 44

匿名结构和联合的典型和现实世界的使用是提供数据的替代视图.例如,在实现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;
Run Code Online (Sandbox Code Playgroud)

如果您将代码期望3D矢量作为指向三个双精度的指针,那么这非常有用.而不是做f(&v.x)丑陋的事情,你可以做到f(v.raw)这一点,这使你的意图清晰.

  • 那只是回避了这个问题.为什么有人会引用关于C的问题的C++标准?想要理解这一点的人必须去咨询正确的标准,以确定他们在这一点上达成一致.或者可以说,"好吧,如果它在C++中是真的,它必须在C中真实......" (3认同)
  • ...可能是成员类型,但不是通过获取由此访问的成员的地址形成的。鉴于编译器正在朝着更积极而不是更理智的方向发展,并且愿意使用对标准极其严格的解释来证明他们的行为,我不相信编译器能够在没有`-fno-的情况下有效地处理上述代码严格别名`。 (2认同)
  • 当这是C问题时,为什么还要引用C ++? (2认同)
  • @davidbowling 我在回答写完两年后回答评论时忘记了它是 C,请原谅我是人类。我没有时间或动力去寻找合适的报价,欢迎您改进答案或提供相关报价或反报价。 (2认同)
  • @艾米丽。- 这完全有道理,无论如何它不会影响答案,只会影响评论。AFAIK,两个标准在这方面都说了几乎相同的事情。我唯一要反对的是行为是 _implementation-defined_ ([not UB](/sf/ask/625289521/ -c11/26361366?noredirect=1#comment67796252_26361366),我认为)由于可能的填充。但是,这在实践中仍然是一个典型的用例,因此似乎是这个问题的一个很好的答案。 (2认同)
  • 为什么需要外部结构?为什么不能直接将 union typedef 为 `vec3d_t`? (2认同)

oua*_*uah 8

struct bla {
    struct { int a; int b; };
    int c;
};
Run Code Online (Sandbox Code Playgroud)

该类型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
Run Code Online (Sandbox Code Playgroud)

  • 和`struct bla {int a; intb; intc;};`有什么区别? (6认同)
  • @Zaibis访问结构的成员没有区别,但具有匿名结构的版本包含额外的信息:`a`和`b`之间存在一些与`c`不存在的逻辑关系 (3认同)
  • @Zaibis-内部结构可以单独命名和使用。一种用例是实现继承(外部结构扩展内部结构)。 (2认同)

小智 6

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

typedef struct {
    union{
        struct {uint8_t a, b, g, r;};
        uint32_t val;
    };
}Color;
Run Code Online (Sandbox Code Playgroud)

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

int main(void)
{
    Color x;
    x.r = 0x11;
    x.g = 0xAA;
    x.b = 0xCC;
    x.a = 0xFF;

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

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

打印 11AACCFF

  • 也许您只是表明您可以做到这一点,但是为什么要使用外部结构呢?如果删除外部结构并 typedef 联合,则代码的行为似乎相同。 (2认同)

sou*_*edi 5

我不确定为什么 C11 允许结构内部有匿名结构。但Linux使用它时带有某种语言扩展

/**
 * 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;
Run Code Online (Sandbox Code Playgroud)

我不确定这个例子是否绝对必要,除了为了清楚地表明意图。

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

#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
Run Code Online (Sandbox Code Playgroud)

即用于随机化字段顺序的语言扩展/编译器插件(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
};
Run Code Online (Sandbox Code Playgroud)