声明中的 _Alignas(N)、[[gnu::aligned(N)]] 和 __attribute__((aligned(N))) 位置

_Alignas(N), [[gnu::aligned(N)]], and __attribute__((aligned(N))) position in declarations

对于以下联盟(没有 AAA 或 BBB):

union AAA bpf_attr {
        struct {    /* Used by BPF_MAP_CREATE */
                uint32_t    map_type;
                uint32_t    key_size;    /* size of key in bytes */
                uint32_t    value_size;  /* size of value in bytes */
                uint32_t    max_entries; /* maximum number of entries
                                            in a map */
        };

        struct {    /* Used by BPF_MAP_*_ELEM and BPF_MAP_GET_NEXT_KEY commands */
                uint32_t            map_fd;
                uint64_t alignas(8) key;
                union {
                        uint64_t alignas(8) value;
                        uint64_t alignas(8) next_key;
                };
                uint64_t            flags;
        };

        struct {    /* Used by BPF_PROG_LOAD */
                uint32_t            prog_type;
                uint32_t            insn_cnt;
                uint64_t alignas(8) insns;     /* 'const struct bpf_insn *' */
                uint64_t alignas(8) license;   /* 'const char *' */
                uint32_t            log_level; /* verbosity level of verifier */
                uint32_t            log_size;  /* size of user buffer */
                uint64_t alignas(8) log_buf;   /* user supplied 'char *'
                                                  buffer */
                uint32_t            kern_version;
                                               /* checked when prog_type=kprobe
                                                  (since Linux 4.1) */
        };
} BBB;

如果我将 _Alignas(8) 放在 AAABBB(或任何其他有效位置),有什么区别吗?该标准似乎没有指定。前者中的任何一个或与[[gnu::aligned(8)]]做同样的事情之间有什么区别吗? GCC 确实指定了一些差异,但我不理解文本:

Where attribute specifiers follow the closing brace, they are considered to relate to the structure, union or enumerated type defined, not to any enclosing declaration the type specifier appears in, and the type defined is not complete until after the attribute specifiers.

编辑:

经过一些测试,似乎(但我一点也不确定):

union alignas(N) u {
        ...
} v;

以上不合法


union u {
        ...
} alignas(N) v;

以上是合法的,因为有一个变量声明。它应该等同于:

union u {
        ...
};
union u alignas(N) v;

union [[gnu::aligned(8)]] u {
        ...
};

以上似乎是合法的,不需要变量声明。


union u {
        ...
} [[gnu::aligned(8)]] v;

以上似乎被忽略了(带有警告),但我不明白为什么;文字说这是允许的,即使它不是首选版本。


还有一件奇怪的事情: 如果您使用 __attribute__((aligned(8))) 而不是 C2x 语法,最后一种形式不会触发警告,顺便说一句,这是 Linux 在其源代码中使用的形式。

我可以测试更多的组合,但这是一个指数级的事情,理论上的答案似乎更合适。

我不知道根据标准和 GNU 文档的理论答案是什么,因为它们不是很详细,但这里有一个广泛的实验答案。

为了我的实验,我编写了以下文件:

align.c:

#include <stdalign.h>
#include <stdio.h>

struct s {
    int a;
    long b;
    char c;
} s_, s__;

__attribute__((aligned(16))) struct t {
    int a;
    long b;
    char c;
} t_, t__;

alignas(16) struct tt {
    int a;
    long b;
    char c;
} tt_, tt__;

[[gnu::aligned(16)]] struct ttt {
    int a;
    long b;
    char c;
} ttt_, ttt__;

struct __attribute__((aligned(16))) u {
    int a;
    long b;
    char c;
} u_, u__;
/*  error: expected ‘{’ before ‘_Alignas’
struct alignas(16) uu {
    int a;
    long b;
    char c;
} uu_, uu__;
*/
struct [[gnu::aligned(16)]] uuu {
    int a;
    long b;
    char c;
} uuu_, uuu__;
/*  error: expected identifier or ‘(’ before ‘{’ token
struct v __attribute__((aligned(16))) {
    int a;
    long b;
    char c;
} v_, v_;
*/
/*  error: expected identifier or ‘(’ before ‘{’ token
struct vv alignas(16) {
    int a;
    long b;
    char c;
} vv_, vv__;
*/
//  warning: ignoring attributes applied to ‘struct vvv’ after definition [-Wattributes]
/*  error: expected identifier or ‘(’ before ‘{’ token
struct vvv [[gnu::aligned(16)]] {
    int a;
    long b;
    char c;
} vvv_, vvv__;
*/
struct w {
    int a;
    long b;
    char c;
} __attribute__((aligned(16))) w_, w__;

struct ww {
    int a;
    long b;
    char c;
} alignas(16) ww_, ww__;
//  warning: ignoring attributes applied to ‘struct www’ after definition [-Wattributes]
struct www {
    int a;
    long b;
    char c;
} [[gnu::aligned(16)]] www_, www__;

struct x {
    int a;
    long b;
    char c;
} x_ __attribute__((aligned(16))), x__;
/*  error: expected ‘;’ before ‘_Alignas’
struct xx {
    int a;
    long b;
    char c;
} xx_ alignas(16), xx__;
*/
struct xxx {
    int a;
    long b;
    char c;
} xxx_ [[gnu::aligned(16)]], xxx__;

struct y {
    int a;
    long b;
    char c;
} y_, __attribute__((aligned(16))) y__;
/*  error: expected identifier or ‘(’ before ‘_Alignas’
struct yy {
    int a;
    long b;
    char c;
} yy_, alignas(16) yy__;
*/
/*  error: expected identifier or ‘(’ before ‘[’ token
struct yyy {
    int a;
    long b;
    char c;
} yyy_, [[gnu::aligned(16)]] yyy__;
*/
struct z {
    int a;
    long b;
    char c;
} z_, z__ __attribute__((aligned(16)));
/*  error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘_Alignas’
struct zz {
    int a;
    long b;
    char c;
} zz_, zz__ alignas(16);
*/
struct zzz {
    int a;
    long b;
    char c;
} zzz_, zzz__ [[gnu::aligned(16)]];


int main(void)
{
    printf("s:   %2zu;  s_:   %2zu; s__:     %2zu\n\n", alignof(struct s), alignof(s_), alignof(s__));
    printf("t:   %2zu;  t_:   %2zu; t__:     %2zu\n", alignof(struct t), alignof(t_), alignof(t__));
    printf("tt:  %2zu;  tt_:  %2zu; tt__:    %2zu\n", alignof(struct tt), alignof(tt_), alignof(tt__));
    printf("ttt: %2zu;  ttt_: %2zu; ttt__:   %2zu\n\n", alignof(struct ttt), alignof(ttt_), alignof(ttt__));
    printf("u:   %2zu;  u_:   %2zu; u__:     %2zu\n", alignof(struct u), alignof(u_), alignof(u__));
    puts("uu:   -");//  printf("uu:  %2zu;  uu_:  %2zu; uu__:    %2zu\n", alignof(struct uu), alignof(uu_), alignof(uu__));
    printf("uuu: %2zu;  uuu_: %2zu; uuu__:   %2zu\n\n", alignof(struct uuu), alignof(uuu_), alignof(uuu__));
    puts("v:    -");//  printf("v:   %2zu;  v_:   %2zu; v__:     %2zu\n", alignof(struct v), alignof(v_), alignof(v__));
    puts("vv:   -");//  printf("vv:  %2zu;  vv_:  %2zu; vv__:    %2zu\n", alignof(struct vv), alignof(vv_), alignof(vv__));
    puts("vvv:  -\n");//    printf("vvv: %2zu;  vvv_: %2zu; vvv__:   %2zu\n", alignof(struct vvv), alignof(vvv_), alignof(vvv__));
    printf("w:   %2zu;  w_:   %2zu; w__:     %2zu\n", alignof(struct w), alignof(w_), alignof(w__));
    printf("ww:  %2zu;  ww_:  %2zu; ww__:    %2zu\n", alignof(struct ww), alignof(ww_), alignof(ww__));
    printf("www: %2zu;  www_: %2zu; www__:   %2zu\n\n", alignof(struct www), alignof(www_), alignof(www__));
    printf("x:   %2zu;  x_:   %2zu; x__:     %2zu\n", alignof(struct x), alignof(x_), alignof(x__));
    puts("xx:   -");//  printf("xx:  %2zu;  xx_:  %2zu; xx__:    %2zu\n", alignof(struct xx), alignof(xx_), alignof(xx__));
    printf("xxx: %2zu;  xxx_: %2zu; xxx__:   %2zu\n\n", alignof(struct xxx), alignof(xxx_), alignof(xxx__));
    printf("y:   %2zu;  y_:   %2zu; y__:     %2zu\n", alignof(struct y), alignof(y_), alignof(y__));
    puts("yy:   -");//  printf("yy:  %2zu;  yy_:  %2zu; yy__:    %2zu\n", alignof(struct yy), alignof(yy_), alignof(yy__));
    puts("yyy:  -\n");//    printf("yyy: %2zu;  yyy_: %2zu; yyy__:   %2zu\n\n", alignof(struct yyy), alignof(yyy_), alignof(yyy__));
    printf("z:   %2zu;  z_:   %2zu; z__:     %2zu\n", alignof(struct z), alignof(z_), alignof(z__));
    puts("zz:   -");//  printf("zz:  %2zu;  zz_:  %2zu; zz__:    %2zu\n", alignof(struct zz), alignof(zz_), alignof(zz__));
    printf("zzz: %2zu;  zzz_: %2zu; zzz__:   %2zu\n", alignof(struct zzz), alignof(zzz_), alignof(zzz__));

    return 0;
}

cc -Wall -Wextra align.c -o align 给出了我上面评论的警告和错误。我必须注释掉一些代码才能编译。

$ ./align
s:    8;    s_:    8;   s__:      8

t:    8;    t_:   16;   t__:     16
tt:   8;    tt_:  16;   tt__:    16
ttt:  8;    ttt_: 16;   ttt__:   16

u:   16;    u_:   16;   u__:     16
uu:   -
uuu: 16;    uuu_: 16;   uuu__:   16

v:    -
vv:   -
vvv:  -

w:   16;    w_:   16;   w__:     16
ww:   8;    ww_:  16;   ww__:    16
www:  8;    www_: 16;   www__:   16

x:    8;    x_:   16;   x__:      8
xx:   -
xxx:  8;    xxx_: 16;   xxx__:    8

y:    8;    y_:    8;   y__:     16
yy:   -
yyy:  -

z:    8;    z_:    8;   z__:     16
zz:   -
zzz:  8;    zzz_:  8;   zzz__:   16

结论:

如果对齐属性位于 struct 关键字之前,它适用于声明的任何变量,但不适用于类型本身。

如果属性位于 struct 关键字和 struct 标记之间,则它适用于类型和声明的任何变量。这对无法编译的 C11 alignas() 无效。

属性不能在{之前。

如果属性紧跟在 } 之后,如果它像在 struct 关键字之前一样工作(仅适用于声明的任何变量),除了旧的 GNU 语法,它适用于类型本身也是。

如果属性在声明的变量之后,它只适用于它。这对无法编译的 C11 alignas() 无效。

如果属性在一个不是第一个声明的变量之前(} 和属性之间至少有一个逗号),它只适用于它旁边的变量。这仅对旧的 GNU 语法有效;其他人不编译。