2个内联函数在C中相互调用的行为
Behaviour of 2 inline functions calling each other in C
我正在尝试学习 C 的更高级方面,并在尝试使用 __inline__
关键字时写了这篇文章:
#include <stdio.h>
void f(int);
void g(int);
__inline__ void f(int egg)
{
printf("f %d\n", egg);
g(egg);
}
__inline__ void g(int egg)
{
printf("g %d\n", egg);
f(egg);
}
int main()
{
f(123);
return 0;
}
我查看了 GNU 手册,发现当标记为 __inline__
的函数无法被替换时,-Winline
编译器选项会发出警告。
我确实期待一个警告,但我用 gcc -ansi -pedantic -Wall -Wextra -Winline -o test test.c
编译了这个程序并且没有警告。
当我 运行 程序时,它在出现分段错误之前打印了一大堆数字,大概是由于超出了递归限制。
我的问题是,gcc
在这种情况下表现如何?如果它内联函数,它怎么知道它命中了两个函数之间的递归调用?
提前致谢
https://gcc.gnu.org/onlinedocs/gcc-7.4.0/gcc/Inline.html#Inline
GCC does not inline any functions when not optimizing unless you specify the ‘always_inline’ attribute for the function
由于您是在未经优化的情况下进行编译,gcc 甚至不会尝试内联您的函数,因此您不会收到未完成的警告。
当我用 -O -Winline
编译您的代码时,我收到了预期的警告:
inline.c: In function ‘main’:
inline.c:8:17: warning: inlining failed in call to ‘f’: call is unlikely and code size would grow [-Winline]
__inline__ void f(int egg)
^
inline.c:24:5: note: called from here
f(123);
^~~~~~
根据我在 goldbolt 中看到的情况,编译器足够聪明,可以理解该代码等同于无限循环(请参阅下面代码中的 .L2)。
当递归函数是 tail recursive
时,这种优化是可能的
这与__inline__
无关。事实上,如果您删除 __inline__
关键字,您将获得相同的优化,并且 也会获得 g
和 f
的汇编代码,它们永远不会 call
ed.
.LC0:
.string "f %d\n"
.LC1:
.string "g %d\n"
main:
sub rsp, 8
.L2:
mov esi, 123
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
mov esi, 123
mov edi, OFFSET FLAT:.LC1
xor eax, eax
call printf
jmp .L2
下面将 gcc
生成的程序集与(右侧)和没有(左侧)__inline__
关键字的 g
和 f
进行比较。
如您所见,main
包含完全相同的代码。唯一的区别是您获得 g
和 f
.
的附加代码
我正在尝试学习 C 的更高级方面,并在尝试使用 __inline__
关键字时写了这篇文章:
#include <stdio.h>
void f(int);
void g(int);
__inline__ void f(int egg)
{
printf("f %d\n", egg);
g(egg);
}
__inline__ void g(int egg)
{
printf("g %d\n", egg);
f(egg);
}
int main()
{
f(123);
return 0;
}
我查看了 GNU 手册,发现当标记为 __inline__
的函数无法被替换时,-Winline
编译器选项会发出警告。
我确实期待一个警告,但我用 gcc -ansi -pedantic -Wall -Wextra -Winline -o test test.c
编译了这个程序并且没有警告。
当我 运行 程序时,它在出现分段错误之前打印了一大堆数字,大概是由于超出了递归限制。
我的问题是,gcc
在这种情况下表现如何?如果它内联函数,它怎么知道它命中了两个函数之间的递归调用?
提前致谢
https://gcc.gnu.org/onlinedocs/gcc-7.4.0/gcc/Inline.html#Inline
GCC does not inline any functions when not optimizing unless you specify the ‘always_inline’ attribute for the function
由于您是在未经优化的情况下进行编译,gcc 甚至不会尝试内联您的函数,因此您不会收到未完成的警告。
当我用 -O -Winline
编译您的代码时,我收到了预期的警告:
inline.c: In function ‘main’:
inline.c:8:17: warning: inlining failed in call to ‘f’: call is unlikely and code size would grow [-Winline]
__inline__ void f(int egg)
^
inline.c:24:5: note: called from here
f(123);
^~~~~~
根据我在 goldbolt 中看到的情况,编译器足够聪明,可以理解该代码等同于无限循环(请参阅下面代码中的 .L2)。 当递归函数是 tail recursive
时,这种优化是可能的这与__inline__
无关。事实上,如果您删除 __inline__
关键字,您将获得相同的优化,并且 也会获得 g
和 f
的汇编代码,它们永远不会 call
ed.
.LC0:
.string "f %d\n"
.LC1:
.string "g %d\n"
main:
sub rsp, 8
.L2:
mov esi, 123
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
mov esi, 123
mov edi, OFFSET FLAT:.LC1
xor eax, eax
call printf
jmp .L2
下面将 gcc
生成的程序集与(右侧)和没有(左侧)__inline__
关键字的 g
和 f
进行比较。
如您所见,main
包含完全相同的代码。唯一的区别是您获得 g
和 f
.