第二十篇 -- 研究下函数(三) —— 内联函数
源代码编译完之后,函数就变成了一个指令的集合。调用函数时,系统将跳转到这些指令集的首地址开始运行。当函数返回时,系统就跳回到函数调用处的下一条指令继续执行。不管调用多少次,每次系统都跳转到同一地址,程序中也只有一个函数的复制。
虽然函数节省了空间,但也不是没有代价。在调用函数的两次跳转过程中,存在一些影响性能的系统开销。如果函数本身非常短小,只有一两条指令,则跳转花费的时间就会占到较大的比重。如果可以避免跳转,则程序的执行效率就会大大提高。例如求和函数:
int add(int a, int b) { return a + b; }
求和在计算机中是最基本的运算,只要一条指令就可以完成。如果写成函数,则需要附加两次跳转、参数传递、函数返回等操作,这将极大地影响效率。所以,对于如此简单的功能,最好不要使用函数,而是直接计算。
在C++中,如果在函数的声明前加上inline关键字,则称为内联函数。对于内联函数,编译器不创建真实的函数,而只是在函数调用处展开(即将函数的代码直接复制到调用处)。这样,在“调用”函数时就不用跳转了,避免了使用真实函数的代价。例如,对于add函数,如果声明为:
inline int add(int a, int b);
函数调用如下:
int x = add(1, 2);
编译后,实际的代码是:
int x = 1 + 2;
尽管在调用处展开内联函数,同复制函数代码是一样的,但还是使用内联函数方便,否则当需要修改代码时,就要在所有用到的地方进行修改。
说明:如果函数是在头文件中定义的,则编译器会自动将该函数视为内联函数。不过,inline关键字和在头文件中定义函数,只是对编译器的一种建议。到底要不要将函数作为内联函数,取决于编译器的判断。有的函数是不适合作为内联函数的,如递归函数,或者有许多语句的函数。这样的函数即便是加了inline关键字,或者定义在头文件中。编译器依然会将其当做非内联的一般函数对待。
.cpp
#include "pch.h" #include <iostream> #include "method.h" using namespace std; int main() { int x = 0, y = 0; cout << "请输入两个整数:" << endl; cin >> x; cin >> y; cout << "最小值:" << min(x, y) << endl; return 0; }
.h
#pragma once int min(int a, int b) { return a < b ? a : b; }
min函数在头文件method.h中定义,编译器自动将其识别为内联函数。并且函数非常短小,所以会在调用处展开。