奇怪的编译器

感觉我今天和编译器杠上了

用途

相信你非常的清楚,但我这里还是要再说一次。

众所周知,函数的调用其实还是挺多步骤的,涉及多次的出栈入栈。

所以如果当函数非常,非常小的时候,这一过程就会非常非常缓慢,那要怎么办呢?我们就可以使用inline关键字,让函数在编译的时候直接与其他代码放到一起就就好了(即内敛)。

举个例子就知道了

inline int maxx(int x,int y){
    return x>y?x:y;
}

int main() {
    cout << maxx(1,2) << endl;
}

那么最后的编译效果可能会是(真实情况请自行查看本地汇编文件)

int main() {
    int tmp;
    if(_x>_y){
        tmp = x;
    }else{
        tmp = y;
    }
    cout << tmp << endl;
}

具体说能优化多少,自己开几个循环测试吧。

啊对了,请不要相信你的编译器能够自动给你添加inline

使用方法

inline关键字

如果使用inline关键字,表示询问编译器是否可以把函数放进来。

实际使用时和上文一样。

inline returnType functionName(){

}

force inline

有些时候编译器比较傻逼,比如说很有可能你在输出一个非常长的字符串的时候就不会让你内联了。

原因有两个:

  1. 如果你的字符串非常长,相当于一个大数组,编译器以为你在搞循(yan)环(se)
  2. printf/puts/cout 会触发一个I/O的循环

这确实可能很慢(想想为什么我们需要快读和快写),所以编译器也不是没有道理

但我们还想内敛怎么办呢?

  • g++/LLVM:使用always_inline特性

    把上面的inline修改成__attribute__((always_inline))即可

  • MSVC:使用__forceinline替换上面的inline就好

背不会怎么办,不想敲这么长怎么办,宏定义呀。

#define Finline __attribute__((always_inline))
//或者
#define Finline __forceinline

什么时候用inline

你得知道,即便用了上面的Finline,你的代码仍然不能按照你想象的走。

怎么不可能,当然可能。当你inline的代码非常的长,且调用了各种各样的for循环。你的编译器可能无法解析这么长的汇编进而终止进程,换成更为保险的方式,也就是不内敛。

所以如果想要保证内敛,记住下面的几条:

  1. 不能存在任何形式的循环语句.
  2. 不能存在过多的条件判断语句.
  3. 函数体不能过于庞大.
  4. 不能对函数进行取址操作.
  5. 内联函数声明必须在调用语句之前.

编译器自动内敛的情况

下面的不一定有效,但理论上会的,这里还是声明一下

  1. 较短的函数
  2. 类/结构体的成员函数
  3. Lambda表达式

需要你明确忘记的东西

  1. extern inline

    一旦inline成功,这个表示会让编译器不再生成函数的汇编代码,就会导致出现无法判断函数重名/函数重载/函数重写的情况。就会导致Bug会从键盘里溢出来的那种(Maybe)。

  2. static inline

    第一个是,我的实验从来没有让gcc内敛成功过

    第二个是,万一真的内敛成功了,没人知道出了Bug怎么调。你想想加了static怎么实现内敛?你函数里面的局部变量要怎么维护?编译器真的有那么强么?你真的认为让编译器隐式的完成这些好么?

    第三个是,网上有很多大神认为它和inline等价....

参考

  1. 网上各位大神的博客
  2. CppPreference中的inline介绍
Last modification:March 7th, 2020 at 12:37 am
Compared with money, your comment could inspire me more.
相较于钱财,你的留言更能激发我创作的灵感