attribute改变编译器的属性

1. 格式:

__attribute__(属性列表)

注:
(1) 前后各是两个下划线;
(2)要指定多个属性,可在双括弧内用逗号将属性分隔开;
(3)可以用来设置函数属性、变量属性和类型属性。
(4)位置放于声明的尾部,例如:

int test_data __attribute__((section("NO_INIT"), zero_init));

如上所示,生成doxygen标准化文档时,可能会把变量定义归结到函数声明列表中,所以可以采用宏的方式进行改写,如下所示:

#define ATTR_NO_INIT_DATA __attribute__((section("NO_INIT"), zero_init))

ATTR_NO_INIT_DATA int test_data;

2. 支持的变量属性

• address (addr)
• aligned (alignment)
• boot
• deprecated
• fillupper
• far
• mode (mode)
• near
• noload
• packed
• persistent
• reverse (alignment)
• section (“section-name”)
• secure
• sfr (address)
• space (space)
• transparent_union
• unordered
• unused
• weak

注:
(1)在使用__attribute__ 参数时,你也可以在参数的前后都加上__(两个下划线),例如,使用__aligned__而不是aligned ,这样,你就可以在相应的头文件里使用它而不用关心头文件里是否有重名的宏定义。
(2)__attribute__(section (“section-name”))是将作用的函数或数据放入指定名为”section_name”输入段。
补充知识:输入段、输出段
输入段和输出段是相对于要生成最终的elf或binary时的Link过程说的,Link过程的输入大都是由源代码编绎生成的目标文件.o,那么这些.o文件中包含的段相对link过程来说就是输入段
而Link的输出一般是可执行文件elf或库等,这些输出文件中也包含有段,这些输出文件中的段就叫做输出段
输入段和输出段本来没有什么必然的联系,是互相独立,只是在Link过程中,Link程序会根据一定的规则(这些规则其实来源于Link Script),将不同的输入段重新组合到不同的输出段中,即使是段的名字,输入段和输出段可以完全不同。
__attribute__的section属性只指定对象的输入段,它并不能影响所指定对象最终会放在可执行文件的什么段。
例如,

  MEMORY_NONE_INIT +0x00 UNINIT 512
  {
     .ANY(NO_INIT)
  }
int test __attribute__((section("NO_INIT"), zero_init));//用于将变量强制定义到"NO_INIT"属性数据节中,zero_init表示将未初始化的变量放到ZI数据节中。因为“NO_INIT”这显性命名的自定义节,具有UNINIT属性。

补充知识:
ARM映像文件的组成:

  • 一个映像文件由一个或多个域(region,也有译为“区”)组成;
  • 每个域包含一个或多个输出段(section,也有译为“节”);
  • 每个输出段包含一个或多个输入段;
  • 各个输入段包含了目标文件中的代码和数据;

    输入段中包含了四类内容:代码、已经初始化的数据、未经过初始化的存储区域、内容初始化为零的存储区域。每个输入段有相应的属性:只读的(RO)、可读写的(RW)以及初始化成零的(ZI)。

    一个输出段中包含了一些列具有相同的RO、RW和ZI属性的输入段。输出段属性与其中包含的输入段属性相同。
    一个域包含一到三个输出段,各个输出段的属性各不相同:RO属性、RW属性和ZI属性。

由上可知,一般情况下,代码会被放到RO属性的输入节,已经初始化的变量会被分配到RW属性输入区,而“ZI”属性输入节可以理解为是初始化成零变量的集合。

Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
                IMPORT  SystemInit
                IMPORT  __main

硬件复位后,首先执行复位处理程序;
初始化堆栈指针、执行完用户定义的底层初始化代码(SystemInit函数)后,接下来的代码调用了__main函数,这里__main函数会调用一些列的C库函数,完成代码和数据的复制、解压缩以及ZI数据的零初始化。

数据的解压缩和复制,其中就包括将储存在ROM/Flash中的已初始化变量的初值复制到相应的RAM中去。
对于一个变量,它可能有三种属性,用const修饰符修饰的变量最可能放在RO属性区,已经初始化的变量会放在RW属性区,那么剩下的变量就要放到ZI属性区了。默认情况下,ZI数据的零初始化会将所有ZI数据区初始化为零,这是每次复位后程序执行C代码的main函数之前,由编译器“自作主张”完成的。所以我们要在C代码中设置一些变量在复位后不被零初始化,那一定不能任由编译器“胡作非为”,我们要用一些规则,约束一下编译器。

扫描二维码关注公众号,回复: 2688941 查看本文章

(3)__attribute__ ((aligned (数字)))可以用来设定结构体的对齐方式;
例如,

 typedef struct
 { 
     int a;
     char b;
     short c;
}__attribute__((aligned(4))) TEST1_T;

sizeof(TEST1_T) = 8;

typedef struct
{
    int a; 
    char b;
    TEST1_T c;
    short d;
}__attribute__((aligned(8))) TEST2_T;

sizeof(TEST2_T) = 24;

猜你喜欢

转载自blog.csdn.net/u010603798/article/details/79390231