5.4 内联函数 引入内联函数的目的是为了解决程序中函数调用效率的问题。 具体分析如下:前面提到的函数是一个更高层次的抽象。 它的引入可以让程序员只关心函数的功能和用法,而不必关心函数的具体实现; 另外,函数的引入可以减少程序的目标代码,实现程序代码和数据的共享。 但是,函数调用也会带来效率降低的问题,因为调用函数实际上是将程序执行序列转移到了函数所在内存中的某个地址。 执行完函数的程序内容后,返回执行程序。 在函数之前。 这种传输操作要求在传输前保护站点并记住执行地址。 转回后,必须先恢复站点,并在原来保存的地址继续执行。 因此,函数调用需要一定的时间和空间开销,这会影响其效率。 尤其是对于一些函数体代码不是很大但调用频繁的函数来说,解决效率问题更为重要。 内联函数的引入其实就是为了解决这个问题。 当程序编译时,编译器将程序中出现的内联函数的调用表达式替换为内联函数的函数体。 显然,这种做法不会造成重定向的问题,但由于编译时将函数体中的代码替换到程序中,因此会增加目标程序代码量,从而增加空间开销,但不会增加时间开销。 函数调用一样大,可见它节省时间的代价是目标代码的增加。
考虑以下 min() 函数 int min( int v1, int v2 ){( v1 < v2 ? v1 : v2 );} 为此类小操作定义函数的好处是: (1) 如果一段代码包含 min () 阅读此类代码并解释其含义比阅读条件运算符的实例并理解代码的作用要容易得多,尤其是对于复杂的表达式; (2) 更改本地化实现比更改应用程序容易得多(其中 300 个应用程序显示要容易得多)。 例如,如果确定测试条件应该是:( v1 == v2 || v1 < v2 ),那么查找这段代码的每次出现将是非常繁琐且容易出错的; (3)语义统一,保证每次测试都以同一种方式实现; (4) 功能可以重复使用,而无需为其他应用程序重写代码。 但将 min() 写为函数有一个严重的缺点。 调用该函数比直接计算条件运算符要慢得多。 不仅必须复制两个实际参数以保存机器的寄存器,而且程序还必须移动到新的位置。 相比之下,直接使用条件表达式进行判断要快得多。 内联函数提供了一个解决方案。 如果将函数指定为函数,则它将在程序中的每个调用点处内联展开。 例如: int = min(i, j); 在编译时扩展为 int = i < j?i : j;min(),从而消除了将其编写为函数的额外执行开销。
在函数声明或定义中的函数返回类型之前添加关键字将 min() 指定为: int min( int v1, int v2 ) { /* ... */ } 但请注意,该指令仅适用于编译器编译器可以选择忽略。 因为将函数声明为函数并不一定适合在调用点进行扩展。 例如,诸如 rgcd() 之类的递归函数不会在调用站点完全展开(尽管其第一次调用会完全展开)。 1,200 行的函数也不太可能在调用站点进行扩展。 通常,机制用于优化小型的、经常调用的只有几行的函数。 它在支持抽象数据类设计中的信息隐藏方面发挥着重要作用。 该函数必须对编译器可见,以便编译器可以在调用站点内联扩展该函数。 与非函数不同,函数必须在调用该函数的每个文本文件中定义。 当然,对于同一个程序的不同文件,如果出现一个函数,它的定义必须是相同的。 对于一个由.cpp和draw.cpp两个文件组成的程序,程序员无法定义这样的min()函数:它引用.cpp中的一个东西; 还有draw.cpp中的另一件事。
如果两个定义不相同,就会出现未定义的行为。 编译器最终将使用这些不同定义中的哪一个作为非函数调用的定义是未定义的,因此程序可能不会按您的预期运行。 的。 为了确保不会发生这种情况,建议将函数的定义放在头文件中。 在调用该函数的每个文件中包含该头文件。 这种方法确保每个函数只有一个定义,程序员不需要重复代码,并且不可能在程序的生命周期内导致无意的不匹配。 5.4.1 如何定义内联函数 定义内联函数的方法很简单,只需在函数定义头前面添加关键字即可。 内联函数的定义方式与一般函数相同。 例如: int (int x, int y, int z){ x+y+z;} 其中 是关键字。 () 是一个内联函数。 [例 5.8] 求 1 到 10 中每个数字的平方的程序。 # int (int x){ (x)*(x);}void main(){for (int i=1;i