Для чего нужны анонимные структуры и союзы в C11?
C11 добавляет, среди прочего, "Анонимные структуры и союзы".
Я искал вокруг, но не мог найти четкого объяснения того, когда анонимные структуры и союзы будут полезны. Я спрашиваю, потому что я не совсем понимаю, что они есть. Я понимаю, что впоследствии они являются структурами или объединениями без имени, но я всегда (должен был?) Воспринимать это как ошибку, поэтому я могу представить себе использование только для именованных структур.
5 ответов
Анонимный союз внутри структур очень полезен на практике. Предположим, что вы хотите реализовать различимый тип суммы (или теговое объединение), агрегат с логическим значением и либо с плавающей точкой, либо как 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
который менее читабелен и более многословен.
Типичное и реальное использование анонимных структур и союзов в мире заключается в предоставлении альтернативного представления данных. Например, при реализации типа 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;
Это полезно, если вы взаимодействуете с кодом, который ожидает трехмерный вектор в виде указателя на три двойных. Вместо того чтобы делать f(&v.x)
что некрасиво, вы можете сделать f(v.raw)
что проясняет ваше намерение.
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
Другая полезная реализация - когда вы имеете дело с цветами rgba, поскольку вам может потребоваться доступ к каждому цвету отдельно или как к одному int.
typedef struct {
union{
struct {uint8_t a, b, g, r;};
uint32_t val;
};
}Color;
Теперь вы можете получить доступ к отдельным значениям 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;
}
Печать 11AACCFF
Я не уверен, почему 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;
Я не уверен, что этот пример строго необходим, за исключением ясности намерения.
РЕДАКТИРОВАТЬ: я нашел другой аналогичный шаблон, который является более четким. Функция анонимной структуры используется с этим атрибутом:
#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
,так далее.
(Я добавил внутреннюю структуру, потому что я думаю, что это один из наиболее часто используемых анонимных структур)