如标题所示,如果我有以下情况会发生什么:
void a(uint8_t i) {
b(i, 0);
}
编译器能否用b(i,0)替换对(i)的调用?
此外,在任何一种情况下,以下都将被视为取代上述内容的良好做法:
#define a(i) b(i, 0)
答案 0 :(得分:3)
这很容易测试。如果对a
的调用在同一编译单元中,则大多数编译器都会对其进行优化。让我们看看会发生什么:
$ cat > foo.c
void b(int, int);
void
a(int a)
{
b(a, 0);
}
void
foo(void)
{
a(17);
}
然后使用一些基本的优化将它编译为汇编程序(我添加了omit-frame-pointer来创建更清晰的输出,你可以验证没有那个标志会发生完全相同的事情):
$ cc -fomit-frame-pointer -S -O2 foo.c
然后查看输出(我清理它并保留代码,生成的汇编程序中有很多注释与这里不相关):
$ cat foo.s
a:
xorl %esi, %esi
jmp b
foo:
xorl %esi, %esi
movl $17, %edi
jmp b
所以我们在这里可以看到编译器首先生成了一个调用a
的正常函数b
(除了它的尾调用优化,所以它是jmp而不是调用)。然后,当编译foo
而不是调用a
时,它只是内联它。
我在这种情况下使用的编译器是一个相对较旧版本的gcc,我也检查过clang做的完全相同。这是非常标准的优化,只要编译器执行任何内联,就会总是内联这样的简单函数。
答案 1 :(得分:2)
这取决于一些事情,尤其是您选择的工具链(编译器,链接器等)和优化设置。
如果编译器可以看到a()
的定义 - 而不仅仅是声明 - 它可能会选择内联a()
。编译器不需要这样做,但是,根据优化设置和编译器本身的实现质量,它可能会。但是,对于现代编译器来说,这是一个相当普遍和直接的优化。
如果函数未声明static
(这非常过于简单地使其成为特定编译单元的本地),那么大多数编译器仍会在对象文件中保留函数a()
的定义,所以它可以与其他目标文件链接(对于其他编译单元)。即使它选择在定义它的编译单元中内联函数调用。
如果函数声明为inline
(并且编译器具有定义的可见性),则实际应用该函数。 inline
是标准允许编译器忽略的提示,无论程序员多么坚定。在实践中,现代编译器通常可以更好地决定内联函数而不是程序员。
如果你有代码存储a()
的地址(例如在指向函数的指针中),编译器可能会选择不内联它。
即使编译器没有内联函数,智能链接器也可能选择(实际上)内联它。然而,大多数C实现使用传统的哑链接器作为工具链的一部分 - 因此这种类型的链接时优化在实践中是不可能的。
即使链接器没有,某些虚拟机主机环境也可能选择在运行时内联。这对于C程序来说是非常不寻常的,但不是超出可能性的范围。
就个人而言,我不担心。除非您拥有真正大量的此类函数,否则编译器是否会执行此类优化,几乎没有可观察到的差异(例如程序性能,大小等)。
我不会使用宏。如果您真的不想在使用, 0
时键入b()
,那么只需编写函数a()
,让编译器担心它。如果性能测量和分析显示您的函数a()
是性能热点,则只尝试进一步手动优化。它可能不会。
或者,使用C ++,并为第二个参数声明函数b()
,默认值为0
。 ;)
答案 2 :(得分:1)