声明中的 __attribute__((packed)) 和 [[gnu::packed]] 位置

Cac*_*ito 2 c attributes gcc struct

packed属性应该放在声明中的什么位置?C2x(C++ 风格)和旧 GNU 属性的使用有什么区别吗?

Cac*_*ito 6

鉴于 GCC 文档不是很清楚,我开发了以下测试程序作为实验答案:

#include <stdio.h>

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

// warning: 'packed' attribute ignored [-Wattributes]
__attribute__((packed)) struct t {
    char a;
    long b;
    char c;
} t_, t__;

// warning: 'packed' attribute ignored [-Wattributes]
[[gnu::packed]] struct ttt {
    char a;
    long b;
    char c;
} ttt_, ttt__;

struct __attribute__((packed)) u {
    char a;
    long b;
    char c;
} u_, u__;

struct [[gnu::packed]] uuu {
    char a;
    long b;
    char c;
} uuu_, uuu__;

// error: expected identifier or '(' before '{' token
/*
struct v __attribute__((packed)) {
    char a;
    long b;
    char c;
} v_, v_;
*/

// error: expected identifier or '(' before '{' token
/*
struct vvv [[gnu::packed]] {
    char a;
    long b;
    char c;
} vvv_, vvv__;
*/

struct w {
    char a;
    long b;
    char c;
} __attribute__((packed)) w_, w__;

// warning: 'packed' attribute ignored for type 'struct www' [-Wattributes]
struct www {
    char a;
    long b;
    char c;
} [[gnu::packed]] www_, www__;

// warning: 'packed' attribute ignored [-Wattributes]
struct x {
    char a;
    long b;
    char c;
} x_ __attribute__((packed)), x__;

// warning: 'packed' attribute ignored [-Wattributes]
struct xxx {
    char a;
    long b;
    char c;
} xxx_ [[gnu::packed]], xxx__;

// warning: 'packed' attribute ignored [-Wattributes]
struct y {
    char a;
    long b;
    char c;
} y_, __attribute__((packed)) y__;

// error: expected identifier or '(' before '[' token
/*
struct yyy {
    char a;
    long b;
    char c;
} yyy_, [[gnu::packed]] yyy__;
*/

// warning: 'packed' attribute ignored [-Wattributes]
struct z {
    char a;
    long b;
    char c;
} z_, z__ __attribute__((packed));

// warning: 'packed' attribute ignored [-Wattributes]
struct zzz {
    char a;
    long b;
    char c;
} zzz_, zzz__ [[gnu::packed]];


int main(void)
{
    printf("s:   %2zu;  s_:   %2zu; s__:     %2zu\n\n", sizeof(struct s), sizeof(s_), sizeof(s__));
    printf("t:   %2zu;  t_:   %2zu; t__:     %2zu\n", sizeof(struct t), sizeof(t_), sizeof(t__));
    printf("ttt: %2zu;  ttt_: %2zu; ttt__:   %2zu\n\n", sizeof(struct ttt), sizeof(ttt_), sizeof(ttt__));
    printf("u:   %2zu;  u_:   %2zu; u__:     %2zu\n", sizeof(struct u), sizeof(u_), sizeof(u__));
    printf("uuu: %2zu;  uuu_: %2zu; uuu__:   %2zu\n\n", sizeof(struct uuu), sizeof(uuu_), sizeof(uuu__));
    puts("v:   -");//  printf("v:   %2zu;  v_:   %2zu; v__:     %2zu\n", sizeof(struct v), sizeof(v_), sizeof(v__));
    puts("vvv: -\n");//  printf("vvv: %2zu;  vvv_: %2zu; vvv__:   %2zu\n", sizeof(struct vvv), sizeof(vvv_), sizeof(vvv__));
    printf("w:   %2zu;  w_:   %2zu; w__:     %2zu\n", sizeof(struct w), sizeof(w_), sizeof(w__));
    printf("www: %2zu;  www_: %2zu; www__:   %2zu\n\n", sizeof(struct www), sizeof(www_), sizeof(www__));
    printf("x:   %2zu;  x_:   %2zu; x__:     %2zu\n", sizeof(struct x), sizeof(x_), sizeof(x__));
    printf("xxx: %2zu;  xxx_: %2zu; xxx__:   %2zu\n\n", sizeof(struct xxx), sizeof(xxx_), sizeof(xxx__));
    printf("y:   %2zu;  y_:   %2zu; y__:     %2zu\n", sizeof(struct y), sizeof(y_), sizeof(y__));
    puts("yyy: -\n");//  printf("yyy: %2zu;  yyy_: %2zu; yyy__:   %2zu\n\n", sizeof(struct yyy), sizeof(yyy_), sizeof(yyy__));
    printf("z:   %2zu;  z_:   %2zu; z__:     %2zu\n", sizeof(struct z), sizeof(z_), sizeof(z__));
    printf("zzz: %2zu;  zzz_: %2zu; zzz__:   %2zu\n", sizeof(struct zzz), sizeof(zzz_), sizeof(zzz__));

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

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

$ ./packed 
s:   24;  s_:   24; s__:     24

t:   24;  t_:   24; t__:     24
ttt: 24;  ttt_: 24; ttt__:   24

u:   10;  u_:   10; u__:     10
uuu: 10;  uuu_: 10; uuu__:   10

v:   -
vvv: -

w:   10;  w_:   10; w__:     10
www: 24;  www_: 24; www__:   24

x:   24;  x_:   24; x__:     24
xxx: 24;  xxx_: 24; xxx__:   24

y:   24;  y_:   24; y__:     24
yyy: -

z:   24;  z_:   24; z__:     24
zzz: 24;  zzz_: 24; zzz__:   24
Run Code Online (Sandbox Code Playgroud)

结论:

与对齐属性不同,它仅适用于类型,不适用于变量。变量被打包当且仅当[1]其类型被打包。这是有道理的;否则,一个变量无法分配给另一个相同类型的变量。

C2x(C++ 风格)属性只能位于struct关键字和结构标记之间。

旧的 GNU 风格属性可以位于 C2x 属性所在的位置,也可以位于右大括号之后。


[1]:

$ wtf is iff
IFF: if and only if
Run Code Online (Sandbox Code Playgroud)