__attribute__((used)) __attribute__((section(x)))_attribute used

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取



在下面的例子中,section 函数属性覆盖了 #pragma arm section 设置。



#pragma arm section code=“foo”

int f2()
{

return 1;

} // into the ‘foo’ area

attribute((section (“bar”))) int f3()

{
return 1;
} // into the ‘bar’ area

int f4()
{
return 1;

} // into the ‘foo’ area

#pragma arm section




---


无论是GNU还是ARM的编译器, 都支持 \_\_attribute\_\_所指定的编译属性,这里着重讲解一下在KEIL 环境下\_\_attribute\_\_中的section的使用方法。  
  


section关键字可以将变量定义到指定的输入段中,下面以具体的例子来讲解section的使用方法.



#define SECTION(level)  attribute((used,section(".fn_cmd."level)))
#define CMD_START_EXPORT(func,func_s) 、
const struct CMD_LIST cmd_fn_##func SECTION(“0.end”) = {func,func_s}
CMD_START_EXPORT(start_fun,“start_fun”);


这个宏定义看起来略微有些复杂,不过不要紧,这里将会一步步的进行分析。


首先来看SECTION这个宏定义,这个宏可以将变量添加到某个输入段中。例如


int a \_\_attribute\_\_((section(“list”))) = 0;  
 这句话的意思是将一个int型的变量a放到名为list的输入段中。


level可以理解为这个输入段的后缀,所以这个输入段最终的名字取决于level,如果这里level = "0.end",那么将这个宏展开得到


 \_\_attribute\_\_((used,\_\_section\_\_(".fn\_cmd.""0.end")))  
 这个时候查看.map文件就可以发现文件里面多了一个名为.fn\_cmd.0.end的输入段,可见level确实就是这个输入段的后半部分。


好了,讲解完SECTION这个宏我们对CMD\_START\_EXPORT(start\_fun,"start\_fun");这个宏进行初步展开可以得到下面的表达式,可以看到就是定义了一个struct CMD\_LIST类型的变量并且使用了section对其属性进行了修饰。


const struct CMD\_LIST cmd\_fn\_start\_fun SECTION("0.end") = {start\_fun,"start\_fun"};  
 这其中有一个地方要注意一下,就是宏表达式中的##符号,这个符号的作用就是将两个字符串进行拼接,例如


#define DEF\_INT(a,b) int a##b = 0  
 DEF\_INT(a,b);  
 那么最终这个宏实现的意思就是定义一个int型的变量,变量的名字叫ab,将这个宏展开就是


int ab = 0;  
 好了知道了##的意思那么就明白了为什么宏展开的结果是定义了一个名为cmd\_fn\_start\_fun的变量了。然后现在在进一步进行宏展开,将其中的SECTION展开得到如下表达式。


const struct CMD\_LIST cmd\_fn\_start\_fun \_\_attribute\_\_((used,\_\_section\_\_(".fn\_cmd.""0.end"))) = {start\_fun,"start\_fun"};  
 这个时候再来看会发现其实就是定义了一个struct CMD\_LIST 类型的变量,变量的名字是cmd\_fn\_start\_fun,并且这个变量被放到了我们所希望的一个输入段.fn\_cmd.0.end中了。


 


那么问题来了,使用section将变量放到我们自定义的输入段中有什么意义呢?  
 我们知道在传统的C语言编程中程序结构是这样的。


int main()  
 {  
     init\_xx();  
     init\_xx();  
     ...  
     while(1)  
     {  
         ...  
     }  
 }  
 先进行若干个初始化程序,然后在循环的执行一段代码。这样开发固然可以,但是这样有一个让人非常不爽的地方,就是每写一个初始化函数都要在main函数中调用,非常的不方便。但是如果使用section先事先将所有的初始化函数加入到我们自己定义的输入段中,然后再在main函数中将这个输入段中初始化函数依次取出,这样就可在不修改main函数的前提下完成对系统的初始化了。


那么section是怎么将这些初始化函数放入输入段中,并且系统还可以获取这些初始化函数的地址呢?在说明之前我先将下面会用到的几个宏定义进行说明一下。


#define SECTION(level)  \_\_attribute\_\_((used,\_\_section\_\_(".fn\_cmd."level)))  
 #define CMD\_START\_EXPORT(func,func\_s) const struct CMD\_LIST cmd\_fn\_##func SECTION("0.end") = {func,func\_s}  
 #define CMD\_EXPORT(func,func\_s) const struct CMD\_LIST cmd\_fn\_##func SECTION("1") = {func,func\_s}  
 #define CMD\_END\_EXPORT(func,func\_s) const struct CMD\_LIST cmd\_fn\_##func SECTION("1.end") = {func,func\_s}  
 当这几个宏被调用时将会产生名为cmd\_fn\_xx的变量,并且这个变量根据被调用的宏来把这个变量放到相应的输入段。例如CMD\_START\_EXPORT这个宏,这个宏其实上面已经讲过了,调用这个宏的时候会产生一个名为cmd\_fn\_xx的变量,并且把这个变量放到了我们自定义的输入段.fn\_cmd.0.end中了。其他两个宏的其实也是差不多的,不同之处就是输入段有些区别。


言归正传,现在继续来讲如何使用section将不同的函数放到我们想要的输入段中,并且获得他们的起始地址和结束地址。


我们可以在每个XXX\_Init函数后面都调用宏CMD\_EXPORT,在调用这个宏时编译器会将XXX\_init这个函数加入到输入段中,由于变量在输入段中的地址是连续的,并且顺序先按 section 名  01234排一遍,section 内再按函数名称排。所以可以按照输入段中顺序来逐个调用这些初始化函数来完成系统的初始化。


具体实现我会根据我的一个自定义命令行的应用来进行部分的说明。


/\*命令函数段起始位置\*/  
 int cmd\_start(void)  
 {  
     return 0;  
 }  
 CMD\_START\_EXPORT(cmd\_start,"int cmd\_start(void)");  
    
 /\*命令函数段结束位置\*/  
 int cmd\_end(void)  
 {  
     return 0;  
 }  
 CMD\_END\_EXPORT(cmd\_end,"int cmd\_end(void)");  
    
    
 void test(void)  
 {  
     printf("hello world\r\n");  
 }  
 CMD\_EXPORT(test,"void test(void)");  
    
 void demo(void)  
 {  
     printf("hello world\r\n");  
 }  
 CMD\_EXPORT(demo,"void demo(void)");  
  
 先定义start和end函数并且分别使用CMD\_START\_EXPORT和CMD\_END\_EXPORT来将其放到输入段.fn\_cmd.0.end和.fn\_cmd.1.end中,按照上面的说明输入段.fn\_cmd.0.end是排在输入段.fn\_cmd.1.end前面的,而使用的CMD\_EXPORT这个宏对应的输入段.fn\_cmd.1是排在.fn\_cmd.0.end和.fn\_cmd.1.end之间的,这里可以看一下编译产生的.map会更加的形象一些。具体在MAP文件的位置如下所示



![img](https://img-blog.csdnimg.cn/img_convert/704eb835977466a81347aa6a5b74209d.png)
![img](https://img-blog.csdnimg.cn/img_convert/a34cdcfcdf5e3ddb337110ff3cb3493c.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值