高级C__attribute__((aligned(n))) 与 #pragma(pack(n))的区别

Posted 从善若水

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了高级C__attribute__((aligned(n))) 与 #pragma(pack(n))的区别相关的知识,希望对你有一定的参考价值。

本人就职于国际知名终端厂商,负责modem芯片研发。
在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。


博客内容主要围绕:
       5G协议讲解
       算力网络讲解(云计算,边缘计算,端计算)
       高级C语言讲解
       Rust语言讲解

attribute((aligned(n))) 与 #pragma(pack(n))的区别

先看个例题一

#pragma pack(push,2)
typedef struct  test{
    char a;
    double b;
    char c;
    int d;
}  __attribute__ ((aligned (32))) test ;
#pragma pack(pop)

问:计算 sizeof(test) ?
答:32

再看例题二

#pragma pack(push,4)
typedef struct  test{
    char a;
    double b;
    char c;
    int d;
}  __attribute__ ((aligned (2))) test ;
#pragma pack(pop)

问:计算 sizeof(test) ?
答:20

再看例题三

typedef struct  subtest{
    char a;
    double b;
    char c;
    int d;
}subtest;

#pragma pack(push,2)
typedef struct  test{
    char a;
    subtest b;
}  __attribute__ ((aligned (4))) test ;
#pragma pack(pop)

问:计算 sizeof(test) ?
答:28

好,不折磨大家了,我们进入正题!!!


一、正确理解__attribute__((aligned(x)))

attribute((aligned(x)))定义的是最小对齐边界,它可以用来修饰structure也可以用来修饰变量或者structure成员变量,它只能增加 structure、变量或者structure成员变量的对齐值(GCC手册说在修饰 typedef 类型时可以增加或减少对齐值)。


二、正确理解 #pragma(pack(n))

#pragma(pack(n))定义的是最大对齐边界,它可以用来修饰structure也可以用来修饰structure成员变量,它可以增加/减少 structure或者structure成员变量的对齐值。


三、详解开篇例题

首先我们要明确在 code顺序上 ,上述例题的 #pragma(pack(n)) 先生效,然后才是 __attribute__((aligned(x)))生效

3.1 例题一解答

#pragma pack(push,2)
typedef struct test{
    char a;
    double b;
    char c;
    int d;
}  __attribute__ ((aligned (32))) test ;
#pragma pack(pop)

① #pragma pack(push,2) 将编译器的最大对齐边界设置为 2
② 以 2 为最大对齐边界后,test 的内存对齐情况(先不考虑 aligned 属性):

typedef struct test{
    char a;   // 1B
    double b; //因为最大对齐是2,所以只需要满足2的倍数,不需要满足8的倍数
    		  //填充 1B ,b 本身占据 8B	
    char c;   //对齐时我们要选择最小的对齐值,对于 c最小的对齐值是1(不是2)
              //无填充,自身占据 1B
    int d;    //对齐值为2,故填充 1B,自身占据 4B
} test ;      //整个结构体对齐值为 2(不是8,因为已经设置了最大对齐值是2)
			  // (1B) + (1B+8B) + (1B) + (1B+4B) + 0B(已经满足2的倍数,无需对结构体进行填充)

也就是说当前(不考虑 aligned 属性)时,test 类型占据 16B,最大对齐值 2 。
③ 最后来看 aligned 属性,我们前面说了,aligned 属性设置的是最小对齐值,也就是说当前类型或变量的对齐值要大于或等于aligned 属性设置的对齐值,所以编译器重新设置 test类型的对齐值为32,则test struct需要填充16B。

✔结果就是32B 。

3.2 例题二解答

例题二不再赘述,对应于 test类型的对齐值,大于 aligned 属性设置的最小对齐值的情况,这种情况下可以忽略aligned 属性

✔结果就是20B 。

3.3 例题三解答

① test 的内存对齐情况(先不考虑 aligned 属性)

typedef struct  subtest{
    char a;
    double b;
    char c;
    int d;
}subtest;

#pragma pack(push,2)
typedef struct  test{
    char a;    // 1B
    subtest b; //对于结构体subtest,原本以 8B做为对齐值
               //但这里设置了最大对齐值2,所以 填充1B,自身占据 24B
} test ;       // (1B) + (1B+24B) + 0B(已经是2的倍数,不需要再填充)
#pragma pack(pop)

② 最后来看 aligned 属性,设置的最小对齐值4 > 2,所以 test 结构体需要填充 2B,以构成4的倍数。

✔结果就是28B 。

四、注意__attribute__ ((aligned (n)))的位置

代码一:

typedef struct  test{
    char a;
    double b;
    char c;
    int d;
} __attribute__ ((aligned (16))) test ;

代码二:

typedef struct  test{
    char a;
    double b;
    char c;
    int d;
} test __attribute__ ((aligned (16)));

代码一修饰的是 struct test类型
代码二修饰的是 test类型

有什么区别呢? 下面的code和输出结果,不再解释,自悟!

代码一:

#include <stdio.h>

typedef struct test{
    char a;
    double b;
    char c;
    int d;
} __attribute__ ((aligned (16))) test ;

int main() {

    test a ;

    printf("%d\\n",sizeof(test));
    printf("%f\\n",(unsigned long long)&a/16.);

    return 0;
}


代码二:

#include <stdio.h>

typedef struct test{
    char a;
    double b;
    char c;
    int d;
} test __attribute__ ((aligned (16)));

int main() {

    test a ;

    printf("%d\\n",sizeof(test));
    printf("%f\\n",(unsigned long long)&a/16.);

    return 0;
}



以上是关于高级C__attribute__((aligned(n))) 与 #pragma(pack(n))的区别的主要内容,如果未能解决你的问题,请参考以下文章

GNU C字节对齐__attribute__((aligned(n))) #pragma pack(n)

__attribute__系列之aligned

C 语言编程 — GCC Attribute 语法扩展

C 语言编程 — GCC Attribute 语法扩展

如果我将字节数组转换为 __attribute__((packed, aligned(2))) 结构会发生啥?

“#pragma pack”和“__attribute __((aligned))”之间的区别是什么?