inline内联函数抽丝剥茧

c/c++中的inline,使用在函数声明处,表示程序员请求编译器在此函数的被调用处将此函数实现插入,而不是像普通函数那样生成调用代码(申请是否有效取决于编译器)。一般地说,这样作的优点是省掉了调用函数的开销;缺点则是可能会增加代所生成目标代码的尺寸(二班的除外,二班情况下,inline函数甚至会返过来降低程序的性能)。

实际上,即使没有手工指定inline函数,编译器一般还会选择一些代码量较小但使用频繁的函数作为inline函数,以此作为性能优化的途径之一。

1. 和带参宏定义(Parameterized Macro)的比较

与带参宏定义相比,inline函数具备以下优点:

参数类型检查:宏定义中所使用的参数仅仅是在宏定义中被替换,不进行任何的类型检查
返回值:宏定义中无法使用return返回
便于调试

  1. 不同编译器下的inline关键字

尽管c/c++有着自己的语言标准和规范,但不同编译器实现中总会有着这样或那样的区别。inline即是一例。

c99标准

inline :用于同一c/cpp文件内部被调用处展开;对外部文件来说函数不可用
static inline :用于在同一c/cpp文件内部被调用处展开;一般情况下,编译器并不会为此函数生成单独的目标代码;如遇到内联函数无法展开,或内联函数以地址形式被调用,则编译器将会为此内联函数生成单独的代码;
简单地说,c99中inline关键字申明的函数一般仅用于同一文件,函数本身不会生成单独的目标代码;static关键字修订后,如果需要,则会生成单独的目标代码。

gcc

inline :对同一c/cpp文件,函数将会在被调用处展开;对外部文件,此函数等同于”extern”函数
static inline :与c99标准中相同
extern inline : 仅用于同一c/c++文件内部,在被调用处展开
gcc中的inline关键字与c99中不同,默认情况下(仅使用inline),在同一文件中被调用处当作内联函数展开,而在外部文件调用中等同于普通extern函数(也就是说会生成单独的目标代码);加static关键字修订后,反而不可应用于外部文件,但如果需要可以生成单独的目标代码;gcc扩展的extern inline模式更是缩小函数的使用仅限于在同文件中展开。

ms vc

ms vc中inline的含义基本与c99中相同,此基础上,作了一定扩展
__inline :等同于inline
__forceinline :强制编译器将函数作为内联函数,除非以下情况
使用了/Ob0选项编译(debug编译默认取值)
函数使用了可变参数
函数本身为递归函数,且未使用#pragma inline_recursion(on)
函数为虚(virtual)函数
程序中使用了地址方式调用了函数

  1. 其他

一般地,内联函数不能是递归函数或调用递归函数(递归调用会给函数体展开带来麻烦)。

inline应当是用于函数声明(Declaration)而非函数实现(Implementation)。但由于由于不同的编译器下inline关键字所修饰函数的调用范围不一致,因此,一种简单的作法是,将inline函数的声明和实现合一。如果确定函数仅应用于同一文件,我们可以将函数的声明和实现都放在.c/.cpp中;否则将其放入.h中(这样,包含了此头文件的任何文件都可以使用它)。

main:
.LFB2:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $32, %rsp
        movl    $2, -20(%rbp)
        movl    $1, -16(%rbp)
        movl    -16(%rbp), %edx
        movl    -20(%rbp), %eax
        movl    %edx, %esi
        movl    %eax, %edi
        call    sum1
        movl    %eax, -12(%rbp)
        movl    -20(%rbp), %eax
        movl    %eax, -8(%rbp)
        movl    -16(%rbp), %eax
        movl    %eax, -4(%rbp)
        movl    -4(%rbp), %eax
        movl    -8(%rbp), %edx
        addl    %edx, %eax
        movl    %eax, -12(%rbp)
        movl    $0, %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc

不能使用inline的情况

当一个函数使用递归方法时,就不能使用inline,原因很简单,递归的函数在编译时,不可能确定其递归次数,否则我们也没必要使用递归调用了。而inline展开函数时,要求展开的结果必须是明确的!这一点非常重要,因为编译产生的结果是明确的。

不适合使用inline函数的几种情况

下面有几种情况之前有同事跟我争论说这么用就是错的,但是在我看来,这么说过于偏激,说不合适更准确一些。

1 引用其他函数

这个显然是不合适的,编写inline就是为了省去函数调用的堆栈内存开销以及调用和返回的时间开销,当你在其中引用了其他函数时,inline一旦展开,还是一个函数,没啥意义啊。这种情况下我还是推荐使用 #define 来辅助吧。

我还看到网上有人说inline函数中的函数会被编译器展开,这种说法似乎是这些人想当然的说法。

2 包含循环体

包含循环体会怎么样呢?

3 函数太长

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值