C11は、とりわけ、「匿名の構造体と共用体」を追加します。
私はざっと見て回ったが、匿名の構造体と組合がいつ役立つかについての明確な説明を見つけることができなかった。私は彼らが何であるかを完全に理解していないので尋ねます。後で名前のない構造体または共用体であることがわかりますが、私は常にそれをエラーとして扱ってきたので、名前付き構造体の使用しか考えられません。
構造内の匿名の結合は、実際には非常に便利です。識別された合計型(またはタグ付き共用体char*
)、ブール値と、ブール値フラグに応じてfloatまたはa (つまり文字列)のいずれかを含む集合体を実装するとします。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
タイプを実装 するためにいくつかを使用します。そしてGNUmakeもそれらを使用します。
匿名の構造体と共用体の典型的かつ現実的な使用法は、データの代替ビューを提供することです。たとえば、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;
これは、3つのdoubleへのポインターとして3Dベクトルを期待するコードにインターフェースする場合に役立ちます。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
もう1つの便利な実装は、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
};
さて、コード内でその構造体から変数を1回だけ宣言する場合、なぜ名前が必要なのですか?
struct {
int a;
struct {
int b;
int c;
} d;
} e,f;
そして、あなたは今、、などのようなものを書くことができe.a
ますf.d.b
。
(これは匿名構造体の最も使用法の1つだと思うので、内部構造体を追加しました)