gcc 中对内联函数的支持

https://blog.csdn.net/hngsc_0/article/details/3509952
大家都知道,在程序中,通过把一个函数声明为内联(inline)函数,就可以让gcc把函数的代码集成(嵌入)到调用该函数的代码中去。这样处理可以去掉函数调用时进入/退出时间开销,从而肯定能够加快执行速度。因此把一个函数声明为内联函数的主要目的就是能够尽量快速地执行函数体。

在gcc中,如果内联函数中有常数值,那么在编译期间gcc就可能用它来进行一些简化操作,因此并非所有内联函数的代码都会被嵌入到调用代码处。内联函数嵌入调用者代码中的操作是一种优化操作,因此只有进行优化编译时才会执行代码的嵌入。若编译过程中没有打开优化选项 "-O",那么内联函数的代码就不会被真正地嵌入到调用者代码中,而是作为普通函数来处理。

在gcc-4.1的手册中指示,需要使用一定的优化级别才能开启某些优化选项,针对内联
函数的优化选项主要有:

'-fno-inline' 忽略代码中的 inline 关键字,该选项使编译器将内联函数以普通函数对待;等同无优化选项时的处理
'-finline-functions' 编译器尝试将'简单'函数集成到调用代码处;如果所有对该函数的调用都被替换而集成在调用者代码中,而且该函数使用static声明了,则该函数就不再像平常那样被编译成汇编代码。具体什么方式,需要查询。必须在-O3选项下才开启
'-fearly-inlining' 加速编译 默认可用
'-finline-limit=N' gcc默认限制内联函数的大小,使用该选项可以控制内联函数的大小;默认值是600,可以设置如下几个值:
max-inline-insns-single N/2
max-inline-insns-auto N/2
min-inline-insns 130 or N/4
max-inline-insns-rtl N

'-fkeep-inline-functions' 将声明为static以及inline的函数放进目标文件中,即使所有对该函数的调用都被集成在调用者代码中;该选项不影响使用extern inline声明的内联函数,该声明属于GNU c扩展。

声明一个函数为内联函数的方法:

inline int func(int a)
{
(
a)++;
}

函数中的某些语句用法可能会使得内联函数的替换操作无法正常进行,或者不适合进行替换操作。例如使用了可变参数,内存分配函数 malloc(),可变长度数据类型变量,非局部 goto语句以及递归函数。编译时可以使用选项 -Winline 让 gcc 对标志成 inline 但不能被替换的函数给出警告信息以及不能替换的原因。如下面例子,它使用了可变长度数据类型变量作为参数:

inline int func(int a)
{
int c = 4;
char p[c]; /
可变长度数组 /
(
a)++;
}
/*

  • 测试函数: 使用 gcc -Winline ... 来提示信息
    */
    int main(void)
    {
    int d = 1;
    func(&d);
    return 0;
    }

假定存储文件为inline.c, gcc -O -Winline -c -o inline.o inline.c 来编译,这里 -O 选项必须打开,否则将没有警告信息输出,因为该函数会被当作普通函数来处理;警告信息如下:

inline.c: In function ‘func’:
inline.c:2: warning: function ‘func’ can never be inlined because it uses alloca (override using the always_inline attribute)
inline.c: In function ‘main’:
inline.c:2: warning: inlining failed in call to ‘func’: function not inlinable
inline.c:11: warning: called from here

可以看出它覆盖了 always_inline 属性,其它无法内联的的用法大家可以自己编写代码测试。
当在一个函数定义中既使用 inline 又使用 static 关键字时,那么如果所有对该内联函数的调用都被替换而集成在调用者代码中,并且程序中没有引用过该内联函数的地址,则该内联函数自身的汇编代码就不会被引用。这时,除非在编译过程中使用选项'-fkeep-inline-functions',否则 gcc 就不会再为该内联函数自身生成实际的汇编代码。由于某些原因,一些对内联函数的调用并不能被集成到函数中去。特别是在内联函数定义之前的调用语句是不会被替换集成的,并且也都不能是递归定义的函数。如果存在一个不能被替换集成的调用,那么内联函数就会像普通函数一样被编译成汇编代码,对于程序中有引用该内联函数的地址的处理同样无法集成。

对于上面这句话的理解,同样我们可以使用上面的 inline.c 函数来测试,当没有加上 static 关键字的时候,可以使用
gcc -O -Winline -S -o inline.s inline.c
来生成汇编程序,可以看到 func 内联函数在汇编代码中同样被生成汇编代码而且被声明为函数;修改 inline.c 增加 static 关键字,gcc -O -Winline -S -o inline2.s inline.c 比较两个文件可以看到 inline2.s 中只有 main 符号,func 的代码直接被集成到 main 中了,此时如果想产生和没有加 static时的效果,编译时就要加上选项 '-fkeep-inline-functions';但是在 C++ 中,该选项会生成一个弱".weak"函数,也就是单独的汇编代码,若不加该选项,内联函数语义等同于 ISO C99 的语义,也就是都不单独生成汇编代码。

/* 不生成单独的汇编代码的版本,或者替换 static 为 extern /
static inline int func(int a)
{
/
int c = 4;
char p[c]; /
可变长度数组 */
/
(
a)++;
}

如果内联函数定义时没有使用 static,那么 gcc 就会假设其它程序文件中也对这个函数有调用。因为一个全局符号只能被定义一次,所以该函数就不能在其它源文件中再进行定义。因此这里对内联函数的调用就不能被替换集成。所以,一个非静态的内联函数总是会被编译出自己的汇编代码来。另外,ISO 标准 C99 中对不使用 static 关键字的内联函数定义等同于这里使用 static
的语义,但是为了保持兼容,最好还是明确指定 static 关键字。

如果在定义一个函数时还指定了 inline 和 extern 关键词,那么该函数定义仅用于内联集成,并且在任何情况下都不会单独产生该函数自身的汇编代码,即使明确引用了该函数的地址也不会产生。这样的一个地址会变成一个外部引用,就好像你仅仅声明了函数而没有定义函数一样。这种用法几乎等同于一个宏定义。
————————————————
版权声明:本文为CSDN博主「hngsc_0」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hngsc_0/article/details/3509952

原文地址:https://www.cnblogs.com/marklove/p/15471033.html