我如何知道内联函数是不是在被调用的地方被实际替换?

Posted

技术标签:

【中文标题】我如何知道内联函数是不是在被调用的地方被实际替换?【英文标题】:How will i know whether inline function is actually replaced at the place where it is called or not?我如何知道内联函数是否在被调用的地方被实际替换? 【发布时间】:2012-05-24 18:25:51 【问题描述】:

我知道内联函数要么在被调用的地方被替换,要么表现得像一个普通函数。

但是我如何知道内联函数是否在被调用的地方实际被替换,作为将内联函数视为内联的决定是在编译时?

【问题讨论】:

你不会的;无论如何,它的行为都必须相同。 见Herb Sutter的Inline Redux 【参考方案1】:

检查生成的代码。如果函数展开,您将看到它的主体,而不是 call 或类似指令。

【讨论】:

call“类似指令”的示例是什么?抱歉,我对组装了解不多。【参考方案2】:

在运行时以编程方式,您不能。 而事情的真相是:你不需要知道

编译器可以选择inline未标记inline的函数或忽略显式标记inline的函数,这完全是编译器的愿望(阅读智慧)&你应该相信编译器会明智地完成其工作。大多数主流编译器都会很好地完成他们的工作。

如果您的问题纯粹是从学术角度来看,那么有几个选项可供选择:


分析生成的汇编代码:

您可以检查汇编代码以检查函数代码是否在调用点内联。

如何生成汇编代码?

对于 gcc: 编译时使用-S 开关。 例如:

g++ -S FileName.cpp

生成的汇编代码被创建为文件FileName.s

对于 MSVC: 从命令行使用 /FA Switch

在生成的汇编代码中查找特定函数是否有call 汇编指令。


使用编译器特定的警告和诊断:

如果某些编译器未能遵守内联函数请求,它们会发出警告。 例如,在 gcc 中,如果编译器没有内联声明为内联的函数,-Winline 命令选项将发出警告。

查看GCC documentation了解更多详情:

-Winline

如果声明为 inline 的函数不能被内联,则发出警告。即使使用此选项,编译器也不会警告系统头文件中声明的内联函数失败。

编译器使用各种启发式方法来确定是否内联函数。例如,编译器会考虑被内联的函数的大小以及在当前函数中已经完成的内联量。因此,源程序中看似微不足道的变化,都会导致-Winline产生的警告出现或消失。

【讨论】:

你的意思是在调试时,在汇编代码部分,我需要检查内联函数是否表现为内联。如果在装配端,如果这个函数被推送并弹出等等......那么它将表现得像一个正常的函数,否则它是内联的...... @Abhineet:是的,或者您可以使用编译器特定的标志来警告无法遵守inline 请求。 +1 表示-Winline。 Here 你有非学术的理由来检查它并强制内联。 (..networking/serialization 库,由于代码大小,默认情况下它不会内联函数..) You don't need to know 不一定。如果你想要一个包装汇编代码的辅助函数,这非常重要。 你说的“你不需要知道”是不对的。在我的代码中,我使用堆栈溢出防护,因此如果函数被内联,则检查代码会产生过多的开销。所以我想知道...【参考方案3】:

是否内联函数由编译器决定。而且由于它是由编译器制作的,所以是的,它只能在编译时制作。

所以,如果你可以使用 -S 选项看到汇编代码(使用 gcc -S 生成汇编代码),你可以看到你的函数是否被内联。

【讨论】:

【参考方案4】:

您可以使用工具列出目标文件中的符号,例如 Linux 上的 nm。如果函数是内联的,它将不会在nm 输出中列出 - 它成为其他函数的一部分。此外,您将无法在调试器中按名称在此函数上放置断点。

【讨论】:

同一个函数可能在一个调用实例中内联,而在另一个调用实例中可能不内联,这完全取决于编译器。因此,使用nm 不是确定函数调用是否被调用的可靠方法确实是内联的。 @Als:好的,如果nm 输出中没有该函数,这意味着它的所有实例都已内联。它仍然提供了一些关于内联的信息。 是的,它提供了一些信息,我想明确指出,使用 nm 可以告诉您翻译单元中函数的 all 调用实例是否为 inline d 与否,它不提供特定调用实例是否为inlined 的信息。【参考方案5】:

使用gdb,如果你不能调用一个函数,它可能的含义之一就是函数是内联的。颠倒推理,如果可以在gdb内部调用函数,则表示该函数没有标记为内联。

【讨论】:

【参考方案6】:

如果您需要确保该函数是内联的并且可以与 MS VC++ 中的专有扩展一起使用,check out the __forceinline declarator。编译器将内联函数,或者,如果它属于记录的特殊情况列表,您将收到警告 - 这样您就会知道内联状态。

不以任何方式认可它。

【讨论】:

是的,__forceinline 删除了编译器的成本/收益计算,并在可能的情况下内联函数。请务必注意,它禁用成本/收益计算,但不能 100% 保证它会被内联。【参考方案7】:
    查看目标文件的大小,内联和非内联是不同的 使用 nm "obj_file" | grep "fun_name",它们也不同 gcc -Winline -O1 与汇编代码比较

【讨论】:

另外,如果内联应该影响运行时,那么在返回类型之后的函数定义中比较有和没有__attribute__ ((noinline))的运行时(注意这是gcc-具体)。【参考方案8】:

以上答案非常有用,我只是添加一些我们在编写内联函数时牢记的要点。

请记住,内联只是对编译器的请求,而不是命令。编译器可以忽略内联请求。 编译器可能不会在这样的情况下执行内联:

1) 如果函数包含循环。 (for, while, do-while)

2) 如果函数包含静态变量。

3) 如果一个函数是递归的。

4) 如果函数返回类型不是void,并且函数体中不存在return语句。

5) 如果函数包含 switch 或 goto 语句。

完整信息:https://www.geeksforgeeks.org/inline-functions-cpp/

【讨论】:

【参考方案9】:

如果函数返回地址,编译器不会将函数内联。

【讨论】:

【参考方案10】:

有一种方法可以在不查看汇编代码的情况下以编程方式确定函数是否内联。此答案取自here。

假设您要检查特定调用是否内联。你会这样。编译器内联函数,但是对于那些被导出的函数(并且几乎所有函数都被导出),它需要维护一个可以从外部调用的非内联可寻址函数代码。

要检查您的函数my_function 是否已内联,您需要将my_function 函数指针(未内联)与PC 的当前值进行比较。以下是我在我的环境中的做法(GCC 7、x86_64):

void * __attribute__((noinline)) get_pc ()  return _builtin_return_address(0); 

void my_function() 
    void* pc = get_pc();
    asm volatile("": : :"memory");
    printf("Function pointer = %p, current pc = %p\n", &my_function, pc);

void main() 
    my_function();

如果一个函数没有内联,PC的当前值和函数指针的值之间的差异应该很小,否则会更大。在我的系统上,当 my_function 未内联时,我得到以下输出:

Function pointer = 0x55fc17902500, pc = 0x55fc1790257b

如果函数是内联的,我会得到:

Function pointer = 0x55ddcffc6560, pc = 0x55ddcffc4c6a

非内联版本差异为0x7b,内联版本差异为0x181f

【讨论】:

以上是关于我如何知道内联函数是不是在被调用的地方被实际替换?的主要内容,如果未能解决你的问题,请参考以下文章

函数式编程———内联函数

java新知识学习:

内联函数和宏

当 C# 调用 c++ 函数时,是不是可以在被调用的 c++ 函数中初始化数组并返回到 C#?

内联函数详解

内联函数和宏定义