您的位置:首页 > 其它

内联函数并不总是内联

2011-01-05 18:55 260 查看
Inline function是在C++中引入的一种机制,它可以拓展函数代码,避免调用函数的额外开销。在Linux环境下,gcc编译选项必须加上优化选项才能使inline有效。如

inline int fun(int i, int j)

{

printf("%d/n", i+j);

return i+j;

}

void main()
{
fun(3,4);
}
不带优化编译选项:g++ -S inline.cc
产生的汇编代码片断如下:

andl $-16, %esp ;栈指针对齐
subl $16, %esp ; 栈指针下移,为函数调用参数预留足够空间

movl $4, 4(%esp) ;参数入栈,后声明先入
movl $3, (%esp) ;参数入栈,先声明后入
call _Z3funii ;函数调用,经过name-mangling处理
即使是inline函数,如果不带优化编译选项,inline关键字也不会对fun产生任何影响
那现在加上优化选项:g++ -S -O inline.cc

产生的汇编代码片断如下:
andl $-16, %esp
subl $16, %esp
movl $7, 4(%esp)
movl $.LC0, (%esp)
call printf
这里已经没有了函数调用的过程,fun函数已经被拓展了。

那现在有一个问题,是不是只要加上inline定义的函数,加上优化选项,就能一定被拓展呢?答案是否定的,因为,加上inline关键字,只是告诉编译器,函数想要被inline,但决定权在编译器手里,由它来决定函数是否inline。这是因为有的函数复杂度很高,这样的话,如果拓展就得不偿失,这样编译器就会忽略inline关键字,不会对函数调用做任何额外的处理,例如定义一个递归函数。还有一种情况是virtual函数,它既可以是内联函数,也可以不是内联函数。

inline int fun(int i, int j)

{

if(i-->0)

fun(i, j);

printf("%d/n", i+j);

return i+j;

}

那现在我们再来看看编译器是否会使函数inline:g++ -S -O inline.cc
产生的汇编代码片断如下:

main:
.LFB12:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $16, %esp
movl $4, 4(%esp)
movl $2, (%esp)
call _Z3funii
movl $6, 4(%esp)
movl $.LC0, (%esp)
call printf
movl $0, %eax
答案很清晰了。
对于virtual函数来说,如果没有触发它的virtual机制,那么,virtual函数是当作正常函数来调用的,这种情况下就可以使用内联机制,如果调用时触发了virtual机制(也就是通过指针或引用来调用),那么这个时候inline的虚函数是没法内联的。也就是说,只有当编译器在编译期能够确定的情况下,inline才能起作用,而触发了virtual机制的地方,调用的函数地址的可变的,没法把它用确定的函数代码替换,只能通过函数调用的方式来使用,所以没法使用inline。如:

class Base
{
public:
virtual void vfun(int i) { printf("%p/n", i); }
};
int main()
{
Base b;
b.vfun(3);
Base *bp = &b;
bp->vfun(4);

return 0;
}
主要汇编代码如下:
andl $-16, %esp

subl $32, %esp
//b.vfun(3); 使用内联
movl $_ZTV4Base+8, 28(%esp)
movl $3, 4(%esp)
movl $.LC0, (%esp)
call printf
//bp->vfun(4); 正常函数调用
movl $4, 4(%esp)
leal 28(%esp), %eax
movl %eax, (%esp)
call _ZN4Base4vfunEi
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: