无论是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文件的位置如下所示

 cmd_fn_cmd_start                         0x080042f0   Data           8  serialcmd.o(.fn_cmd.0.end)cmd_fn_test                              0x080042f8   Data           8  application.o(.fn_cmd.1)cmd_fn_demo                              0x08004300   Data           8  application.o(.fn_cmd.1)cmd_fn_cmd_end                           0x08004308   Data           8  serialcmd.o(.fn_cmd.1.end)

可以看到输入段.fn_cmd.0.end的地址确实是在最靠前的,而.fn_cmd.1.end的地址确实是排在最后面的。在输入段.fn_cmd.1中的那些数据就是我们要用到的数据。 由于我所放到输入段中的变量是struct CMD_LIST类型的,占8个字节,定义如下


typedef void (*fun)();
struct CMD_LIST
{fun funs;const INT8 *cmd;
};

所以每个变量的大小是8,也就是每个变量在内存中的偏移是8,所以就明白了为什么变量的地址是每次递增8个字节了。

下面这个函数就是从输入段中获取数据,并进行相应处理的函数。

const static struct CMD_LIST *CmdList;
static UINT8 CmdSize = 0;/*命令函数初始化*/
void SerialCmdInit(void)
{const struct CMD_LIST *cmd_ptr;CmdList = &cmd_fn_cmd_start;CmdList++;for (cmd_ptr = CmdList; cmd_ptr < &cmd_fn_cmd_end;cmd_ptr++){/*这里如果用于初始化的话可以使用下面这种方式来执行初始化函数,因为我的应用并不是用于初始            化,所以就没有进行函数调用。(*cmd_ptr->fun)();*/CmdSize++;}
}

其中cmd_fn_cmd_start是在.fn_cmd.0.end这个输入段中的,而各个要执行的函数是在.fn_cmd.1这个输入段中的,cmd_fn_cmd_end作为结束的标志在.fn_cmd.1.end输入段中。其余的就不做过多讲解了,从上面的.map文件中的地址很容易就可以看出在得到了起始地址和结束地址之后怎样一个个遍历这些函数。

最后再说一下之前__attribute__((used,__section__(".fn_cmd."level)))中used的意思。
unused:表示该函数或变量可能不使用,这个属性可以避免编译器产生警告信息。
used: 向编译器说明这段代码有用,即使在没有用到的情况下编译器也不会警告。

这里很有必要提一下,我在使用KEIL进行编译的时候由于没有加used导致变量被编译器给优化掉了,所以在有些时候这个关键字还是有必要添加的。

__attribute__((section(x))) 使用详解相关推荐

  1. h5中的结构元素header、nav、article、aside、section、footer详解

    文章目录 header元素 nav元素 article元素 aside元素 section元素 footer元素 结构元素不具有任何样式,只是使页面元素的的语义更加明确 header元素 header ...

  2. php模板引擎循环start,PHP模板引擎Smarty内建函数section,sectionelse用法详解

    本文实例讲述了PHP模板引擎Smarty内建函数section,sectionelse用法.分享给大家供大家参考,具体如下: section 是 Smarty 模板中除了 foreach 以外的另一种 ...

  3. PHP模板引擎Smarty内建函数section,sectionelse用法详解

    本文实例讲述了PHP模板引擎Smarty内建函数section,sectionelse用法.分享给大家供大家参考,具体如下: section 是 Smarty 模板中除了 foreach 以外的另一种 ...

  4. __attribute__之section详解

    前言 第一次接触 "section" 是在公司的一个STM32的项目代码中,前工程师将所有的初始化函数都使用的"section"进行设定了属性.当时知道其目的, ...

  5. html里的section可以设置id,html5中section元素详解

    html5中section元素详解 一.总结 一句话总结: section元素 用来定义文章中的章节(通常应该有标题和段落内容) section元素的作用就是给内容分段,给页面分区 1.section ...

  6. Vue项目使用symbol的方式引入svg图标和svg path大法d属性详解,section标签

    symbol标签 svg path大法d属性详解 section标签

  7. Linux ELF 详解2 -- Section Header Section

    上一篇:ELF 详解1 – ELF Header ELF Section Header & Section 先看 Section Header 的定义 typedef struct {Elf3 ...

  8. HTML5中section由什么组成,html5中section元素详解

    html5中section元素详解 一.总结 一句话总结: section元素 用来定义文章中的章节(通常应该有标题和段落内容) section元素的作用就是给内容分段,给页面分区 1.section ...

  9. iOS开发之runtime(16):设置/获取section数据详解

    背景 在前面的文章中,笔者有讲解如何设置以及获取一个section的数据,demo如下: #import <Foundation/Foundation.h> #import <dlf ...

最新文章

  1. 为什么说Python是一门动态语言--Python的魅力
  2. Spring @Autowired和@Resource
  3. NYOJ 659 判断三角形
  4. Extjs4.2——Panel
  5. python对图片颜色校正_使用Python PIL更改图像色调
  6. @RequestBody的与@RequestParam.
  7. Spring Boot war方式打包发布
  8. 深入理解 PHP7 unset 真的会释放内存吗?
  9. ZOJ1151 Word Reversal(没法提交)
  10. 先进pid控制matlab仿真_PID控制原理 三 (控制系统硬件选择与仿真)
  11. 接口设计的一些小总结
  12. 时域,空域,频域的基本概念
  13. 【寒江雪】SSL通信详解
  14. 昭阳k20-80拆机过程
  15. 计算机 去掉快捷方式箭头,去掉桌面快捷方式小箭头方法(无需修改注册表)
  16. 手机屏幕按键测试软件,涨见识了——手机居然能作为维修检测工具来用!
  17. 新华三智慧计算联盟,集结!
  18. 图片放大模糊怎么办?这个方法了解一下
  19. 【学习笔记】山东大学生物信息学-05 高通量测序技术介绍 + 06 统计基础与序列算法(原理)
  20. 两组回归系数差异检验_如何检验两个回归系数的差异性?我做调节分析。

热门文章

  1. 专访Alan Clark:迎接更加开放的开源大时代
  2. 应届程序员:考研or工作or考公,但行好事,莫问前程
  3. CLM model tips
  4. BDG邦德外汇:如何让自己成为赚钱高手
  5. 掌握这些方法和技巧,你的自学才会更加有效率!
  6. Unique Constraint
  7. 一个女生写的如何追mm.看完后嫩头青变高手.【男人必看】
  8. JUC并发编程笔记1__JUC概述、虚假唤醒问题、JUC的生产者和消费者关系、8锁问题(对象锁、类锁区别)
  9. 揭开ZigBee 2006协议栈Z-Stack的”开源“面纱
  10. git安装及第一次上传代码遇到的问题