C11增加了“匿名结构体和联合体”等新特性。
我搜索了一下,但没有找到一个清晰的解释来说明何时使用匿名结构体和联合体会有用。 我问这个问题是因为我不完全理解它们是什么。 我知道它们是没有名称的结构体或联合体,但我通常将其视为错误,因此我只能想象命名结构体的用途。
在实践中,结构体内部的匿名联合非常有用。考虑您想要实现一个区分的总和类型(或标记联合),它是一个聚合体,具有一个布尔值和一个浮点数或者一个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.f
和 ch->u.s
这种不太易读且更冗长的代码。
另一种实现一些带标记的联合类型的方法是使用强制类型转换。 Ocaml运行时提供了许多示例。
Common Lisp的SBCL实现确实使用一些union
来实现带标记的联合类型。而GNU make也使用它们。
匿名结构体和联合体的典型实际用途是提供数据的另一种视角。例如,在实现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
的情况下有用地处理上述代码。 - supercatvec3d_t
? - Mad Physiciststruct 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
a
和b
之间存在某种逻辑关系,而在使用c
时不存在这种关系。请注意不改变原意,并使翻译通俗易懂。 - ouah另一个有用的实现是当你处理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
/**
* 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
};
struct {
int a;
struct {
int b;
int c;
} d;
} e,f;
现在你可以编写像e.a
,f.d.b
等的内容。
(我添加了内部结构,因为我认为这是匿名结构最常用的用法之一)
.d
、e
和f
。它们具有匿名类型,但这是另一回事。 - Alex Celeste