__attribute__是GNU C扩展下一大特性机制,用于设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute )。
__attribute__前后以两个下划线 __ 标识,后端跟 () 说明跟随参数属性。语法格式为:

__attribute__ (( attribute-list ))

常见的属性设置如下:

1、aligned

aligned用于变量、结构或联合,设定一个指定大小的对齐格式,以字节为单位(参数有效值为2的幂值),比如:

#include <stdio.h>

void printf_red(const char *s)
{
    printf("\033[0m\033[1;31m%s\033[0m", s);
}

struct test_struct
{
    char flag;
    int state;
    int length;
} __attribute__((aligned(8)));

struct test_struct test;

int main(int argc, char **argv)
{
    printf_red("==================================================\r\n");
    printf("This struct size is %d\r\n", sizeof(test));
    printf_red("==================================================\r\n");
    return 0;
}

最终运行结果如下:

1695803582277.png

aligned属性使编译器尽其所能的确保在分配变量空间时,为其设置4字节对齐大小。如果属性后不跟指定数字,编译器将依据你的目标机器情况使用最大最有益的对齐方式。

#include <stdio.h>

void printf_red(const char *s)
{
    printf("\033[0m\033[1;31m%s\033[0m", s);
}

struct test_struct
{
    char flag;
    int state;
    int length;
} __attribute__((aligned(2)));

struct test_struct test;

int main(int argc, char **argv)
{
    printf_red("==================================================\r\n");
    printf("This struct size is %d\r\n", sizeof(test));
    printf_red("==================================================\r\n");
    return 0;
}

上面的代码中指定了使用2字节对齐,但是编译器会按照最优的方式进行对齐,最终结果就是按照4字节进行对齐,上面代码的运行结果如下:

1695804048973.png

需要注意的是,attribute 属性的效力与编译器有关,如果编译器最大只支持16 字节对齐,那么此时定义32 字节对齐也是无济于事的。

2、packed

属性packed常常用于修饰结构体,表示使用最小可能的对齐,也就是取消结构体的字节对齐

#include <stdio.h>

void printf_red(const char *s)
{
    printf("\033[0m\033[1;31m%s\033[0m", s);
}

struct test_struct
{
    char flag;
    int state;
    int length;
} __attribute__((packed));

struct test_struct test;

int main(int argc, char **argv)
{
    printf_red("==================================================\r\n");
    printf("This struct size is %d\r\n", sizeof(test));
    printf_red("==================================================\r\n");
    return 0;
}

取消字节对齐后,成员变量按照1字节对齐,运行结果如下:

1695804263575.png

其作用类似于 #pragma pack(1),但是需要注意的是,packed仅仅对修饰的对象起作用,对内嵌结构体是不生效的,例如下面的代码:


![https://duojiyun.xinmouren.cn/blog/23/9/d41d8cd98f00b204e9800998ecf8427e.png](https://duojiyun.xinmouren.cn/blog/23/9/d41d8cd98f00b204e9800998ecf8427e.png)#include <stdio.h>

void printf_red(const char *s)
{
    printf("\033[0m\033[1;31m%s\033[0m", s);
}
struct test_struct_1
{
    char t;
    int length;
};
struct test_struct
{
    char flag;
    int state;
    int length;
    struct test_struct_1 t_1;
} __attribute__((packed));

struct test_struct test;

int main(int argc, char **argv)
{
    printf_red("==================================================\r\n");
    printf("This struct size is %d\r\n", sizeof(test));
    printf_red("==================================================\r\n");
    return 0;
}

上述代码中,外层的结构体被packed修饰,取消了字节对齐,但是内部嵌套的那个结构体并未被修饰,所以依旧按照4字节进行对齐,上面代码运行结果如下:

1695804634684.png

3、at

at常用于嵌入式的开发,用来设置变量的绝对地址,指定某个变量处于RAM或 FLASH 里面的某个给定的地址,语法为__attribute__((at(addr)))。

作用有:

  1. 定位到flash中,一般用于固化的信息,如出厂设置的参数以及flash标记等等
  2. 定位到RAM中,一般用于内存池的实现
//以下代码适配STM32

const uint8_t array[] __attribute__((at(0x08002800)))={0xA5,0x5A};//将数组直接写到指定的flash地址
uint8_t __attribute__((aligned(32))) ccm_ram_base[CCM_RAM_MAX_SIZE] __attribute__((at(0X10000000))); // 内部CCM内存池

以内存池为例,经过面的内存分配以后,就可以自己进行内存的申请和释放了

//以下仅作演示

static uint32_t ccm_ram_mem_malloc(uint32_t size)
{
    signed long offset = 0;
    uint32_t nmemb;     // 需要的内存块数
    uint32_t cmemb = 0; // 连续空内存块数
    uint32_t i;
    if (!mallco_dev.memrdy)
        mallco_dev.init(); // 未初始化,先执行初始化
    if (size == 0)
        return 0XFFFFFFFF;             // 不需要分配
    nmemb = size / CCM_RAM_BLOCK_SIZE; // 获取需要分配的连续内存块数
    if (size % CCM_RAM_BLOCK_SIZE)
        nmemb++;
    for (offset = CCM_RAM_BLOCK_SIZE - 1; offset >= 0; offset--) // 搜索整个内存控制区
    {
        if (!mallco_dev.memmap[offset])
            cmemb++; // 连续空内存块数增加
        else
            cmemb = 0;      // 连续内存块清零
        if (cmemb == nmemb) // 找到了连续 nmemb 个空内存块
        {
            for (i = 0; i < nmemb; i++) // 标注内存块非空
            {
                mallco_dev.memmap[offset + i] = nmemb;
            }
            return (offset * CCM_RAM_BLOCK_SIZE); // 返回偏移地址
        }
    }
    return 0XFFFFFFFF; // 未找到符合分配条件的内存块
}

定位至flash中,需要增加const关键字修饰

4、section

该属性用于修饰函数或者变量,在编译时将被修饰的变量或是函数编译至特定段中。语法使用__attribute__((section(“xxx”))).

uint8_t ret attribute((section(“.test.”))) = 0;

该段语句意为定义uint8_t类型变量,其初值为0,编译放入".test."的输入段中。

例如想要实现类似RThread中的自动初始化机制或者分散函数的位置,让应用程序无需显式调用某些函数,都可以使用此功能

可以现在连接文件中定义一个段,再用宏将所有需要执行的函数包裹,这样在编译阶段就能拿到所有需要运行的函数指针(函数名称就是一个指针),我们只需一个for循环即可执行所有的函数