# 工欲善其事必先利其器-C语言拓展--嵌入式C语言(九)

工欲善其事必先利其器-C语言拓展–嵌入式C语言(九)

文章内容全部来自–>《嵌入式C语言自我修养——从芯片、编译器到操作系统》 王利涛前辈的,超级推荐

内联函数

这个万一就是真的有点意思了,来来接着看看

这一节,我们接着介绍与内联函数相关的两个属性:noinline和always_inline。这两个属性的用途是告诉编译器,在编译时,对我们指定的函数内联展开或不展开。

static inline __attribute__((noinline)) int func();
static inline __attribute__((always_inline)) int func();

属性声明:noinline

一个使用inline声明的函数被称为内联函数,内联函数一般前面会有static和extern修饰。

使用inline声明一个内联函数,和使用关键字register声明一个寄存器变量一样,只是建议编译器在编译时内联展开

**使用关键字register修饰一个变量,只是建议编译器在为变量分配存储空间时,将这个变量放到寄存器里,**这会使程序的运行效率更高。

当然以上都是建议,至于会不会这样做,视具体情况而定,编译器要根据寄存器资源是否紧张、这个变量的类型及是否频繁使用来做权衡。

使用inline时也是一样,编译器也会根据实际情况,如函数体大小、函数体内是否有循环结构、是否有指针、是否有递归、函数调用是否频繁来做决定。

如GCC编译器,一般是不会对函数做内联展开的,只有当编译优化等级开到-O2以上时,才会考虑是否内联展开。

但是在我们使用noinline和always_inline对一个内联函数作显式属性声明后,编译器的编译行为就变得确定了:使用noinline声明,就是告诉编译器不要展开;使用always_inline属性声明,就是告诉编译器要内联展开。

那说了这么多,什么时内联函数欸?

在说这个事情之前,你得先明白个事情,函数在执行得过程中去调用其他的函数时是有很大得开销的。

看看调用过程:

(1)保存当前函数现场。
(2)跳到调用函数执行。
(3)恢复当前函数现场。
(4)继续执行当前函数。

需要不断地保存现场、恢复现场,这就是函数调用带来的开销。

对于一般的函数来说这个事情也就那么回事,但是如果你整个只有一句的短小精悍的代码,这样反复调用很不划算啊。

于是我们将这样的函数设置为内联函数,像宏一样在调用出直接展开执行,这样就可以减少函数调用的开销。

内联函数与宏对比

那为啥不直接整个宏算了?

来嘛来看看内联函数独特的优势:

● 参数类型检查:内联函数虽然具有宏的展开特性,但其本质仍是函数,在编译过程中,编译器仍可以对其进行参数检查,而宏不具备这个功能。

● 便于调试:函数支持的调试功能有断点、单步等,内联函数同样支持。

● 返回值:内联函数有返回值,返回一个结果给调用者。这个优势是相对于ANSI C说的,因为现在宏也可以有返回值和类型了,如前面使用语句表达式定义的宏。

● 接口封装:有些内联函数可以用来封装一个接口,而宏不具备这个特性。

看来还是很具有优势的,那我们看看编译器对内联函数的处理

编译器对内联函数的处理

虽然可以通过inline关键字将一个函数声明为内联函数,但编译器不一定会对这个函数做内联展开。编译器也要根据实际情况进行评估,**权衡展开和不展开的利弊,**并最终决定要不要展开。

内联函数并不是完美无瑕的,也有一些缺点。

内联函数会增大程序的体积,如果在一个文件中多次调用内联函数,多次展开,那么整个程序的体积就会变大,在一定程度上会降低程序的执行效率。同时内联函数往往降低了函数的复用性。

编译器在对内联函数做展开时,除了检测用户定义的内联函数内部是否有指针、循环、递归,还会在函数执行效率和函数调用开销之间进行权衡。

考虑因素:

● 函数体积小。

● 函数体内无指针赋值、递归、循环等语句。

● 调用频繁。

当我们认为一个函数体积小,而且被大量频繁调用,应该做内联展开时,就可以使用static inline关键字修饰它。但编译器不一定会做内联展开,如果你想明确告诉编译器一定要展开,或者不展开,就可以使用noinline或always_inline对函数做一个属性声明。

内联函数为什么定义在头文件中

如果你曾经看过这个玩意,你会发现其很多的内联函数都在头文件中,为什么?还非得用个static修饰一下。

因为它是一个内联函数,可以像宏一样使用,任何想使用这个内联函数的源文件,都不必亲自再去定义一遍,直接包含这个头文件,即可像宏一样使用。

使用inline定义的内联函数,编译器不一定会内联展开,那么当一个工程中多个文件都包含这个内联函数的定义时,编译时就有可能报重定义错误。而使用static关键字修饰,则可以将这个函数的作用域限制在各自的文件内,避免重定义错误的发生。

(static 修饰了那这个外面的函数不久用不成了?包含头文件不就可以了,static限制了使用的范文,定义在头文件,可以让其他文件包含。)

【学习资料】《嵌入式C语言自我修养——从芯片、编译器到操作系统》

  • 0
    点赞
  • 0
    收藏
  • 打赏
    打赏
  • 0
    评论
宋宝华嵌入式 C/C++语言精华文章集锦 C/C+语言 struct 深层探索 ............................................................................2 C++中 extern "C"含义深层探索........................................................................7 C 语言高效编程的几招...............................................................................11 想成为嵌入式程序员应知道的 0x10 个基本问题 .........................................................15 C 语言嵌入式系统编程修炼...........................................................................22 C 语言嵌入式系统编程修炼之一:背景篇 ............................................................22 C 语言嵌入式系统编程修炼之二:软件架构篇 ........................................................24 C 语言嵌入式系统编程修炼之三:内存操作 ..........................................................30 C 语言嵌入式系统编程修炼之四:屏幕操作 ..........................................................36 C 语言嵌入式系统编程修炼之五:键盘操作 ..........................................................43 C 语言嵌入式系统编程修炼之六:性能优化 ..........................................................46 C/C++语言 void 及 void 指针深层探索 .................................................................50 C/C++语言可变参数表深层探索 .......................................................................54 C/C++数组名与指针区别深层探索 .....................................................................60 C/C++程序员应聘常见面试题深入剖析(1) ..............................................................62 C/C++程序员应聘常见面试题深入剖析(2) ..............................................................67 一道著名外企面试题的抽丝剥茧 ......................................................................74 C/C++结构体的一个高级特性――指定成员的位数 .......................................................78 C/C++中的近指令、远指针和巨指针 ...................................................................80 从两道经典试题谈 C/C++中联合体(union)的使用 ......................................................81 基于 ARM嵌入式 Linux 移植真实体验 ................................................................83 基于 ARM嵌入式 Linux 移植真实体验(1)――基本概念 ...........................................83 基于 ARM嵌入式 Linux 移植真实体验(2)――BootLoader .........................................96 基于 ARM嵌入式 Linux 移植真实体验(3)――操作系统 ..........................................111 基于 ARM嵌入式 Linux 移植真实体验(4)――设备驱动 ..........................................120 基于 ARM嵌入式 Linux 移植真实体验(5)――应用实例 ..........................................135 深入浅出 Linux 设备驱动编程 .......................................................................144 1.Linux 内核模块..............................................................................144 2.字符设备驱动程序 ...........................................................................146 3.设备驱动中的并发控制 .......................................................................151 4.设备的阻塞与非阻塞操作 .....................................................................157

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:数字20 设计师:CSDN官方博客 返回首页
评论

打赏作者

摸肚子的小胖子

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值