C++随笔:内联(inline)

发布时间 2023-05-16 18:09:18作者: imxiangzi

内联(inline)

    内联函数以牺牲源文件大小为代价减小程序运行的时间,内联函数类似于一种编译器控制的复制/粘贴功能,当一个函数内部逻辑执行的消耗要远小于函数调用本身的消耗时,会额外增加栈空间的消耗,而被标记为内联的函数,编译器会直接将函数的定义“粘贴”到代码的调用处,直接在调用处将代码展开,这样就不需要执行函数调用了,但也增加了冗余的代码量。

    同时,内联也是宏的一种增强,宏替换是简单的文本替换,不会对类型进行检查,且有时候会带来逻辑上的错误或歧义,比如经典的乘法宏二义性定义:

#define MUL(x, y) x * y
int main()
{
// 按照正常逻辑,3+4的和,与5相乘,结果是35
// 但宏替换仅仅是简单的文本替换,实际执行的是:3 + 4 * 5,结果为23
cout << MUL(3 + 4, 5) << endl;
return 0;
}

// 踩坑之后,也可以通过规范化来避免宏定义的这种问题,总结就是:多用括号!
#define MUL(x, y) ((x) * (y))


内联增强了宏的功能:

① 内联可以打断点调试,宏不能打断点调试。(可以加log输出,宏定义可以替换为一个函数,宏定义本身不是函数)

② 内联会做类型和语法检查,宏不会。比如:

#define MACRO_PRINT(n) \
{ \
printf("%d\n", n); \
}

inline void InlinePrint(int n)
{
printf("%d\n", n);
}

int main()
{
// 会有参数不匹配的警告,但在可以的情况下,不会进行隐式转换
// 输出-1099511628,不同环境下不同,总之不是一个正常的数字
MACRO_PRINT(66.6666);
// 也会有参数不匹配的警告,但是double到int可以隐式转换
// 输出66,隐式转换导致数据丢失
InlinePrint(66.6666);
return 0;
}


需要特别注意的是:

① 与宏定义在预编译阶段处理不同,内联在编译阶段处理。

② 内联函数是一个函数,将宏的定义内容书写为一个函数,不代表这个宏就表示一个函数,它只是用来替换的一段文本。

③ 对编译器来说,内联只是建议,编译器会根据实际情况决定是否内联,比如编译器自身的设定,又比如判断函数内部代码量或执行逻辑的开销远大于调用开销等情况,编译器会自行判断是否接受程序员对函数的内联标记。有意思的是,编译器也可能会自行判断将一个没有被标记为内联的函数标记为内联。并且,内联只会在release版本生效,debug版本都当作普通函数处理,这里可以看出,内联本质上是一种编译器对代码的优化。

④ 内联针对的是函数,不是只有类的成员函数可以内联,普通定义的函数也可以内联,类中声明和定义的函数,会被编译器默认为内联(不过还是加上关键字,便于阅读)。

⑤ 内联的函数声明定义最好在同一个文件或模块中,内联必须对编译器可见,如果声明定义分开,不会有语法和编译上的问题,但连接时,因为找不到内联函数的定义,无法在调用处将代码展开,会报连接错误。当然,也可以将声明和定义的文件或模块都包含进来解决这个问题,但最好都放在同一个文件或模块中。

⑥ 在代码量较多(比如超过10行),或有循环,递归时,不要用内联,用了也不一定会被编译器内联。

 

 

B站个人主页:https://space.bilibili.com/513745196

知乎个人主页:https://www.zhihu.com/people/LingZhiZi

微信公众号搜索:灵知子 作者:灵知子 https://www.bilibili.com/read/cv13225561 出处:bilibili

 

 

https://www.bilibili.com/read/cv13225561