Answers:
首先,inline
对函数的说明只是一个提示。编译器可以(并且经常)完全忽略inline
限定符的存在或不存在。话虽如此,编译器可以内联递归函数,就像它可以展开无限循环一样。它只需要在将其“展开”功能的级别上设置一个限制。
优化的编译器可能会转换以下代码:
inline int factorial(int n)
{
if (n <= 1)
{
return 1;
}
else
{
return n * factorial(n - 1);
}
}
int f(int x)
{
return factorial(x);
}
变成这段代码:
int factorial(int n)
{
if (n <= 1)
{
return 1;
}
else
{
return n * factorial(n - 1);
}
}
int f(int x)
{
if (x <= 1)
{
return 1;
}
else
{
int x2 = x - 1;
if (x2 <= 1)
{
return x * 1;
}
else
{
int x3 = x2 - 1;
if (x3 <= 1)
{
return x * x2 * 1;
}
else
{
return x * x2 * x3 * factorial(x3 - 1);
}
}
}
}
在这种情况下,我们基本上将函数内联了3次。一些编译器确实执行此优化。我记得MSVC ++有一个设置来调整将在递归函数上执行的内联级别(我认为最多20个)。
的确,如果您的编译器不能明智地运行,它可能会尝试inline
递归地插入d函数的副本,从而创建无限大的代码。但是,大多数现代编译器都会意识到这一点。他们可以:
对于情况2,#pragma
您可以设置许多编译器来指定执行此操作的最大深度。在gcc中,您还可以从命令行使用--max-inline-insns-recursive
(在此处查看更多信息)传递它。
编译器创建一个调用图;当检测到一个循环调用自身时,在某个深度(n = 1、10、100,无论编译器调整到什么深度)之后,将不再内联该函数。
请参阅已经给出的答案,以了解为何通常无法正常工作。
作为“脚注”,您可以使用模板metaprogramming实现所需的效果(至少对于您作为示例使用的阶乘)。从维基百科粘贴:
template <int N>
struct Factorial
{
enum { value = N * Factorial<N - 1>::value };
};
template <>
struct Factorial<0>
{
enum { value = 1 };
};
“编译器如何决定是否内联函数?”
这取决于编译器,指定的选项,编译器的版本号,可能有多少可用内存等。
该程序的源代码仍然必须遵守内联函数的规则。无论是否内联该函数,您都必须为内联它的可能性做准备(一些未知次数)。
Wikipedia声明递归宏通常是非法的,但了解却很少。C和C ++可以防止递归调用,但是通过包含看起来像是递归的宏代码,翻译单元不会变得非法。在汇编程序中,递归宏通常是合法的。