【问题标题】:(How) Can I inline a particular function call?(如何)我可以内联特定的函数调用吗?
【发布时间】:2013-01-28 21:29:48
【问题描述】:

假设我有一个在程序的多个部分中被调用的函数。还假设我对该函数有一个特定的调用,该函数位于对性能极为敏感的代码部分(例如,一个迭代数千万次且每一微秒都很重要的循环)。有没有一种方法可以强制编译器(在我的情况下为gcc)内联该单个特定函数调用,而不内联其他函数调用?

编辑:让我完全清楚:这个问题不是关于强制 gcc(或任何其他编译器)内联对函数的所有调用;相反,它是关于请求编译器将特定调用内联到函数。

【问题讨论】:

  • 手动操作? (只需在此处插入代码。)
  • 为了清楚起见,您希望对foo() 的特定调用是内联的,但对foo() 的其他调用正常进行?
  • 对于那些投票结束的人:这不是作为重复提出的问题的重复。那个询问如何内联函数(在对函数的所有调用中)。这个问题询问如何内联对该函数的一个特定调用。
  • @JensGustedt:观察到 GCC 在当前编译中内联调用并不能保证未来版本的 GCC 将内联调用,或者如果编译开关发生更改,GCC 将内联调用。
  • @JensGustedt:不,GCC 不会在“如果有意义”的情况下内联调用。 GCC 既不具备一般推理能力,也不具备做出最佳决策所需的所有信息。 GCC 具有启发式方法,这可能很好,但不如消息灵通、经验丰富的程序员对目标平台、应用程序的使用方式、应用程序将使用特定应用程序的次数以及在什么情况下具有额外的知识。功能等。此外,所有这些都超出了问题的前提,它清楚地询问了如何内联特定调用。

标签: c function inline


【解决方案1】:

在 C(相对于 C++)中,没有标准的方法来建议函数应该被内联。它只是供应商特定的扩展。

无论你如何指定它,据我所知编译器总是会尝试内联每个实例,所以只使用该函数一次:

原文:

   int MyFunc()  { /* do stuff */  }

改为:

   inline int MyFunc_inlined()  { /* do stuff */  }

   int MyFunc()  { return MyFunc_inlined(); }

现在,在您想要内联它的地方,使用MyFunc_inlined()

注意:上面的“inline”关键字只是 gcc 用于强制内联的任何语法的占位符。如果 H2CO3 删除的答案是可信的,那就是:

static inline __attribute__((always_inline)) int MyFunc_inlined()  { /* do stuff */  }

【讨论】:

  • 有。 inline 关键字是标准 C99。它确实向编译器提供了有关内联的提示,它只是不强制内联。
  • inline 是标准建议。它的有效性是实现定义的。
  • @CarlNorum,这甚至不是一个建议。它使内联成为可能,因为它允许您将函数定义放在头文件中而不会产生“多符号”错误。
  • “所以只使用该功能一次”?这不是完全违背了使用 at 函数的要点,例如代码重用吗?
  • @InkBlend:关键是要有两个版本的函数,一个有 always_inline 属性,只在你想强制内联的地方使用,另一个没有 always_inline 属性,是用于内联不重要的地方。为了避免重复代码,非always_inline 版本是通过简单地调用always_inline 版本来创建的。所以代码被重用了。
【解决方案2】:

可以启用内联每个翻译单元(但不是每个调用)。虽然这不是问题的答案,而且是一个丑陋的把戏,但它conforms to C standard 并且作为相关的东西可能很有趣。

诀窍是在不想内联的地方使用extern 定义,在需要内联的地方使用extern inline

例子:

$ cat func.h 
int func();

$ cat func.c 
int func() { return 10; }

$ cat func_inline.h 
extern inline int func() { return 5; }

$ cat main.c       
#include <stdio.h>

#ifdef USE_INLINE
# include "func_inline.h"
#else
# include "func.h"
#endif

int main() { printf("%d\n", func()); return 0; }

$ gcc main.c func.c && ./a.out
10                                                // non-inlined version

$ gcc main.c func.c -DUSE_INLINE && ./a.out
10                                                // non-inlined version

$ gcc main.c func.c -DUSE_INLINE -O2 && ./a.out
5                                                 // inlined!

您也可以使用非标准属性(例如 GCC 中的 __attribute__(always_inline)))来定义 extern inline,而不是依赖于 -O2

顺便说一句,诀窍是used in glibc

【讨论】:

    【解决方案3】:

    在 C 中强制内联函数的传统方法是根本不使用函数,而是使用宏之类的函数。这种方法总是会内联函数,但是像宏这样的函数存在一些问题。例如:

    #define ADD(x, y) ((x) + (y))
    printf("%d\n", ADD(2, 2));
    

    还有 inline 关键字,它是在 C99 标准中添加到 C 中的。值得注意的是,Microsoft 的 Visual C 编译器不支持 C99,因此您不能将 inline 与那个(悲惨的)编译器一起使用。 内联 仅向编译器提示您希望内联函数 - 它不保证它。

    GCC 有一个扩展,它要求编译器内联函数。

    inline __attribute__((always_inline)) int add(int x, int y) {
        return x + y;
    }
    

    为了更简洁,您可能需要使用宏:

    #define ALWAYS_INLINE inline __attribute__((always_inline))
    ALWAYS_INLINE int add(int x, int y) {
        return x + y;
    }
    

    我不知道有一种可以在某些调用上强制内联的函数的直接方法。但是您可以像这样组合这些技术:

    #define ALWAYS_INLINE inline __attribute__((always_inline))
    #define ADD(x, y) ((x) + (y))
    ALWAYS_INLINE int always_inline_add(int x, int y) {
        return ADD(x, y);
    }
    
    int normal_add(int x, int y) {
        return ADD(x, y);
    }
    

    或者,你可以这样:

    #define ADD(x, y) ((x) + (y))
    int add(int x, int y) {
        return ADD(x, y);
    }
    
    int main() {
        printf("%d\n", ADD(2,2));    // always inline
        printf("%d\n", add(2,2));    // normal function call
        return 0;
    }
    

    另外,请注意,强制内联函数可能不会使您的代码更快。内联函数会导致生成更大的代码,这可能会导致更多的缓存未命中。 我希望这会有所帮助。

    【讨论】:

    • 与其他答案一样,这不能回答我的问题。知道如何强制内联函数很有用,但我想知道是否可以强制对该函数的 特定调用 内联。
    • InkBlend,我一开始并没有完全理解你的问题,我已经编辑了我的答案以更好地回答你的问题。
    【解决方案4】:

    答案取决于您的职能、您的要求和职能的性质。你最好的选择是:

    • 告诉编译器您希望它内联
    • 将函数设为静态(注意 extern,因为在某些模式下它的语义在 gcc 中会发生一些变化)
    • 设置编译器选项以通知优化器您想要内联,并适当设置内联限制
    • 在编译器上打开任何无法内联的警告
    • 验证输出(您可以检查生成的汇编程序)该函数是内联的。

    编译器提示

    这里的答案仅涵盖内联的一方面,即编译器的语言提示。当标准说:

    将函数设为内联函数表明对该函数的调用应为 尽可能快。这些建议的有效程度是 实现定义

    这可能是其他更强提示的情况,例如:

    • GNU 的__attribute__((always_inline)):通常,除非指定优化,否则函数不会内联。对于声明为内联的函数,即使未指定优化级别,此属性也会内联函数。
    • 微软__forceinline:__forceinline 关键字覆盖了成本/收益分析,而是依赖于程序员的判断。使用 __forceinline 时要小心。不分青红皂白地使用 __forceinline 可能会导致代码变大,但性能提升有限,在某些情况下甚至会降低性能(例如,由于增加了更大的可执行文件的分页)。

    即使这两者都依赖于可能的内联,并且关键是依赖于编译器标志。要使用内联函数,您还需要了解编译器的优化设置。

    值得一提的是,内联也可用于为您所在的编译单元提供现有函数的替换。当近似答案对您的算法足够好时,可以使用此方法,或者可以在使用本地数据结构的更快方法。

    内联定义 提供了外部定义的替代方案,翻译人员可以使用它来实现 在同一个翻译单元中对函数的任何调用。未指定是否调用 函数使用内联定义或外部定义。

    有些函数不能内联

    例如,对于不能内联的 GNU 编译器函数有:

    请注意,函数定义中的某些用法可能使其不适合内联替换。这些用法包​​括:可变参数函数、alloca 的使用、可变长度数据类型的使用(请参阅可变长度)、计算 goto 的使用(请参阅作为值的标签)、非局部 goto 的使用和嵌套函数(请参阅嵌套函数)。当标记为 inline 的函数无法替换时,使用 -Winline 会发出警告,并给出失败的原因。

    所以即使always_inline 也可能无法达到您的预期。

    编译器选项

    使用 C99 的内联提示将依赖于您向编译器指示您正在寻找的内联行为。

    例如 GCC 有:

    -fno-inline, -finline-small-functions, -findirect-inlining, -finline-functions, -finline-functions-called-once, -fearly-inlining, -finline-limit=n

    Microsoft 编译器还具有决定内联有效性的选项。一些编译器还允许优化考虑运行配置文件。

    我确实认为在更广泛的程序优化背景下看内联是值得的。

    防止内联

    您提到您不想内联某些函数。这可以通过设置类似__attribute__((always_inline)) 而不打开优化器来完成。但是,您可能会想要优化器。这里的一种选择是暗示你不想要它:__attribute__ ((noinline))。但为什么会这样呢?

    其他形式的优化

    您还可以考虑如何重组循环并避免分支。分支预测可以产生巨大的影响。有关这方面的有趣讨论,请参阅:Why is it faster to process a sorted array than an unsorted array?

    然后,您还可以展开更小的内部循环并查看不变量。

    【讨论】:

      【解决方案5】:

      有一个内核源代码以一种非常有趣的方式使用#defines 来定义多个具有相同主体的不同命名函数。 这解决了需要维护两个不同功能的问题。 (我忘记是哪一个了……)。我的想法也是基于同样的原则。

      使用定义的方式是在需要的编译单元上定义内联函数。为了演示该方法,我将使用一个简单的函数:

      int add(int a, int b);
      

      它的工作原理是这样的:在头文件中创建一个函数生成器#define,并声明该函数的普通版本的函数原型(未内联)。

      然后你声明两个独立的函数生成器,一个用于普通函数,一个用于内联函数。您声明为static __inline__ 的内联函数。当您需要在其中一个文件中调用内联函数时,您可以使用生成器定义来获取它的源代码。在所有其他需要使用普通函数的文件中,只需在原型中包含标题即可。

      代码已在以下位置进行测试:

      Intel(R) Core(TM) i5-3330 CPU @ 3.00GHz
      Kernel Version: 3.16.0-49-generic
      GCC 4.8.4
      

      代码价值超过一千字,所以:

      文件层次结构

      +
      | Makefile
      | add.h
      | add.c
      | loop.c
      | loop2.c
      | loop3.c
      | loops.h
      | main.c
      

      添加.h

      #define GENERATE_ADD(type, prefix)  \
          type int prefix##add(int a, int b) { return a + b; }
      
      #define DEFINE_ADD()            GENERATE_ADD(,)
      #define DEFINE_INLINE_ADD()     GENERATE_ADD(static __inline__, inline_)
      
      int add(int, int);
      

      这看起来不太好,但减少了维护两个不同功能的工作。该函数在GENERATE_ADD(type,prefix) 宏中完全定义,因此如果您需要更改该函数,只需更改此宏,其他所有内容都会更改。

      接下来,将从add.c 调用DEFINE_ADD() 以生成add 的普通版本。 DEFINE_INLINE_ADD() 将允许您访问一个名为 inline_add 的函数,该函数与您的普通 add 函数具有相同的签名,但它具有不同的名称(inline_ 前缀)。

      注意:我在使用 -O3 标志时没有使用 __attribute((always_inline))__ - __inline__ 完成了这项工作。但是,如果您不想使用-O3,请使用:

      #define DEFINE_INLINE_ADD()     GENERATE_ADD(static __inline__ __attribute__((always_inline)), inline_)
      

      add.c

      #include "add.h"
      
      DEFINE_ADD()
      

      DEFINE_ADD() 宏生成器的简单调用。这将声明函数的普通版本(不会被内联的那个)。

      循环.c

      #include <stdio.h>
      #include "add.h"
      
      DEFINE_INLINE_ADD()
      
      int loop(void)
      {
      
          register int i;
      
          for (i = 0; i < 100000; i++)
              printf("%d\n", inline_add(i + 1, i + 2));
      
          return 0;
      }
      

      loop.c 中,您可以看到对DEFINE_INLINE_ADD() 的调用。这使该函数可以访问inline_add 函数。编译时,所有inline_add函数都会被内联。

      loop2.c

      #include <stdio.h>
      #include "add.h"
      
      int loop2(void)
      {
          register int i;
      
          for (i = 0; i < 100000; i++)
              printf("%d\n", add(i + 1, i + 2));
      
          return 0;
      }
      

      这是为了表明您可以正常从其他文件中使用add的普通版本。

      loop3.c

      #include <stdio.h>
      #include "add.h"
      
      DEFINE_INLINE_ADD()
      
      int loop3(void)
      {
      
          register int i;
      
          printf ("add: %d\n", add(2,3));
          printf ("add: %d\n", add(4,5));
          for (i = 0; i < 100000; i++)
              printf("%d\n", inline_add(i + 1, i + 2));
      
          return 0;
      }
      

      这是为了表明您可以在同一个编译单元中使用两个函数,但是其中一个函数将被内联,而另一个则不会(参见 GDB disass 详情如下)。

      循环.h

      /* prototypes for main */
      int loop (void);
      int loop2 (void);
      int loop3 (void);
      

      main.c

      #include <stdio.h>
      #include <stdlib.h>
      #include "add.h"
      #include "loops.h"
      
      int main(void)
      {
          printf("%d\n", add(1,2));
          printf("%d\n", add(2,3));
      
          loop();
          loop2();
          loop3();
          return 0;
      }
      

      生成文件

      CC=gcc
      CFLAGS=-Wall -pedantic --std=c11
      
      main: add.o loop.o loop2.o loop3.o main.o
          ${CC} -o $@ $^ ${CFLAGS}
      
      add.o: add.c 
          ${CC} -c $^ ${CFLAGS}
      
      loop.o: loop.c
          ${CC} -c $^ -O3 ${CFLAGS}
      
      loop2.o: loop2.c 
          ${CC} -c $^ ${CFLAGS}
      
      loop3.o: loop3.c
          ${CC} -c $^ -O3 ${CFLAGS}
      

      如果您使用__attribute__((always_inline)),您可以将Makefile 更改为:

      CC=gcc
      CFLAGS=-Wall -pedantic --std=c11
      
      main: add.o loop.o loop2.o loop3.o main.o
          ${CC} -o $@ $^ ${CFLAGS}
      
      %.o: %.c
          ${CC} -c $^ ${CFLAGS}
      

      编译

      $ make
      gcc -c add.c -Wall -pedantic --std=c11
      gcc -c loop.c -O3 -Wall -pedantic --std=c11
      gcc -c loop2.c -Wall -pedantic --std=c11
      gcc -c loop3.c -O3 -Wall -pedantic --std=c11
      gcc -Wall -pedantic --std=c11   -c -o main.o main.c
      gcc -o main add.o loop.o loop2.o loop3.o main.o -Wall -pedantic --std=c11
      

      反汇编

      $ gdb main
      (gdb) disass add
      
         0x000000000040059d <+0>: push   %rbp
         0x000000000040059e <+1>: mov    %rsp,%rbp
         0x00000000004005a1 <+4>: mov    %edi,-0x4(%rbp)
         0x00000000004005a4 <+7>: mov    %esi,-0x8(%rbp)
         0x00000000004005a7 <+10>:mov    -0x8(%rbp),%eax
         0x00000000004005aa <+13>:mov    -0x4(%rbp),%edx
         0x00000000004005ad <+16>:add    %edx,%eax
         0x00000000004005af <+18>:pop    %rbp
         0x00000000004005b0 <+19>:retq   
      
      (gdb) disass loop
      
         0x00000000004005c0 <+0>: push   %rbx
         0x00000000004005c1 <+1>: mov    $0x3,%ebx
         0x00000000004005c6 <+6>: nopw   %cs:0x0(%rax,%rax,1)
         0x00000000004005d0 <+16>:mov    %ebx,%edx
         0x00000000004005d2 <+18>:xor    %eax,%eax
         0x00000000004005d4 <+20>:mov    $0x40079d,%esi
         0x00000000004005d9 <+25>:mov    $0x1,%edi
         0x00000000004005de <+30>:add    $0x2,%ebx
         0x00000000004005e1 <+33>:callq  0x4004a0 <__printf_chk@plt>
         0x00000000004005e6 <+38>:cmp    $0x30d43,%ebx
         0x00000000004005ec <+44>:jne    0x4005d0 <loop+16>
         0x00000000004005ee <+46>:xor    %eax,%eax
         0x00000000004005f0 <+48>:pop    %rbx
         0x00000000004005f1 <+49>:retq   
      
      (gdb) disass loop2
      
         0x00000000004005f2 <+0>: push   %rbp
         0x00000000004005f3 <+1>: mov    %rsp,%rbp
         0x00000000004005f6 <+4>: push   %rbx
         0x00000000004005f7 <+5>: sub    $0x8,%rsp
         0x00000000004005fb <+9>: mov    $0x0,%ebx
         0x0000000000400600 <+14>:jmp    0x400625 <loop2+51>
         0x0000000000400602 <+16>:lea    0x2(%rbx),%edx
         0x0000000000400605 <+19>:lea    0x1(%rbx),%eax
         0x0000000000400608 <+22>:mov    %edx,%esi
         0x000000000040060a <+24>:mov    %eax,%edi
         0x000000000040060c <+26>:callq  0x40059d <add>
         0x0000000000400611 <+31>:mov    %eax,%esi
         0x0000000000400613 <+33>:mov    $0x400794,%edi
         0x0000000000400618 <+38>:mov    $0x0,%eax
         0x000000000040061d <+43>:callq  0x400470 <printf@plt>
         0x0000000000400622 <+48>:add    $0x1,%ebx
         0x0000000000400625 <+51>:cmp    $0x1869f,%ebx
         0x000000000040062b <+57>:jle    0x400602 <loop2+16>
         0x000000000040062d <+59>:mov    $0x0,%eax
         0x0000000000400632 <+64>:add    $0x8,%rsp
         0x0000000000400636 <+68>:pop    %rbx
         0x0000000000400637 <+69>:pop    %rbp
         0x0000000000400638 <+70>:retq   
      
      (gdb) disass loop3
      
         0x0000000000400640 <+0>: push   %rbx
         0x0000000000400641 <+1>: mov    $0x3,%esi
         0x0000000000400646 <+6>: mov    $0x2,%edi
         0x000000000040064b <+11>:mov    $0x3,%ebx
         0x0000000000400650 <+16>:callq  0x40059d <add>
         0x0000000000400655 <+21>:mov    $0x400798,%esi
         0x000000000040065a <+26>:mov    %eax,%edx
         0x000000000040065c <+28>:mov    $0x1,%edi
         0x0000000000400661 <+33>:xor    %eax,%eax
         0x0000000000400663 <+35>:callq  0x4004a0 <__printf_chk@plt>
         0x0000000000400668 <+40>:mov    $0x5,%esi
         0x000000000040066d <+45>:mov    $0x4,%edi
         0x0000000000400672 <+50>:callq  0x40059d <add>
         0x0000000000400677 <+55>:mov    $0x400798,%esi
         0x000000000040067c <+60>:mov    %eax,%edx
         0x000000000040067e <+62>:mov    $0x1,%edi
         0x0000000000400683 <+67>:xor    %eax,%eax
         0x0000000000400685 <+69>:callq  0x4004a0 <__printf_chk@plt>
         0x000000000040068a <+74>:nopw   0x0(%rax,%rax,1)
         0x0000000000400690 <+80>:mov    %ebx,%edx
         0x0000000000400692 <+82>:xor    %eax,%eax
         0x0000000000400694 <+84>:mov    $0x40079d,%esi
         0x0000000000400699 <+89>:mov    $0x1,%edi
         0x000000000040069e <+94>:add    $0x2,%ebx
         0x00000000004006a1 <+97>:callq  0x4004a0 <__printf_chk@plt>
         0x00000000004006a6 <+102>:cmp    $0x30d43,%ebx
         0x00000000004006ac <+108>:jne    0x400690 <loop3+80>
         0x00000000004006ae <+110>:xor    %eax,%eax
         0x00000000004006b0 <+112>:pop    %rbx
         0x00000000004006b1 <+113>:retq   
      

      符号表

      $ objdump -t main | grep add
      0000000000000000 l    df *ABS*  0000000000000000              add.c
      000000000040059d g     F .text  0000000000000014              add
      
      $ objdump -t main | grep loop
      0000000000000000 l    df *ABS*  0000000000000000              loop.c
      0000000000000000 l    df *ABS*  0000000000000000              loop2.c
      0000000000000000 l    df *ABS*  0000000000000000              loop3.c
      00000000004005c0 g     F .text  0000000000000032              loop
      00000000004005f2 g     F .text  0000000000000047              loop2
      0000000000400640 g     F .text  0000000000000072              loop3
      
      $ objdump -t main | grep main
      main:     file format elf64-x86-64
      0000000000000000 l    df *ABS*  0000000000000000              main.c
      0000000000000000       F *UND*  0000000000000000              __libc_start_main@@GLIBC_2.2.5
      00000000004006b2 g     F .text  000000000000005a              main
      
      $ objdump -t main | grep inline
      $
      

      嗯,就是这样。经过 3 个小时的敲击键盘试图弄明白,这是我能想到的最好的。请随时指出任何错误,我将非常感激。我对这个特殊的内联one函数调用非常感兴趣。

      【讨论】:

      • 这……令人着迷。不久前我问过这个问题,当时我还没有真正了解 C 编程(现在我很难找到我真的只需要内联某些函数调用的情况)。尽管如此,这种技术还是很酷的,现在我真的想找到一个用例,这样我就可以利用它了。感谢您的努力!
      • @InkBlend 很高兴我能帮上忙!
      • @andygavin 为什么是-1?问题是:“如何内联 one 函数调用”。有人回答了。如果您打开O3,您将把大部分控制权交给编译器,而这不是他想要的。当然,O3 将内联大多数功能....这是 hack。 在站立之前先了解是一个好方法......
      • @andygavin 如果你用这样一个简单的函数把O3 转过来,它总是会被内联。目的是展示如何做到这一点。如果您不想使用O3,只需使用__attribute__((always_inline)),就像我在答案中所说的那样。
      • @EnzoFerber,(不)幸运的是,我最近发现了 Lisp 的乐趣,从那以后几乎没有时间用 C 编写代码。如果我回到 C 并找到它的用途,我会告诉你,但现在我能想到的只有 lets 和 lambdas...
      【解决方案6】:

      如果您不介意同一个函数有两个名称,您可以在函数周围创建一个小包装器,以“阻止”always_inline 属性影响每次调用。在我的示例中,loop_inlined 将是您将在性能关键部分使用的名称,而普通的 loop 将用于其他任何地方。

      内联.h

      #include <stdlib.h>
      
      static inline int loop_inlined() __attribute__((always_inline));
      int loop();
      
      static inline int loop_inlined() {
          int n = 0, i;
          for(i = 0; i < 10000; i++) 
              n += rand();
          return n;
      }
      

      inline.c

      #include "inline.h"
      
      int loop() {
          return loop_inlined();
      }
      

      main.c

      #include "inline.h"
      #include <stdio.h>
      
      int main(int argc, char *argv[]) {
          printf("%d\n", loop_inlined());
          printf("%d\n", loop());
          return 0;
      }
      

      无论优化级别如何,这都有效。在 Intel 上使用 gcc inline.c main.c 编译给出:

      4011e6:       c7 44 24 18 00 00 00    movl   $0x0,0x18(%esp)
      4011ed:       00
      4011ee:       eb 0e                   jmp    4011fe <_main+0x2e>
      4011f0:       e8 5b 00 00 00          call   401250 <_rand>
      4011f5:       01 44 24 1c             add    %eax,0x1c(%esp)
      4011f9:       83 44 24 18 01          addl   $0x1,0x18(%esp)
      4011fe:       81 7c 24 18 0f 27 00    cmpl   $0x270f,0x18(%esp)
      401205:       00
      401206:       7e e8                   jle    4011f0 <_main+0x20>
      401208:       8b 44 24 1c             mov    0x1c(%esp),%eax
      40120c:       89 44 24 04             mov    %eax,0x4(%esp)
      401210:       c7 04 24 60 30 40 00    movl   $0x403060,(%esp)
      401217:       e8 2c 00 00 00          call   401248 <_printf>
      40121c:       e8 7f ff ff ff          call   4011a0 <_loop>
      401221:       89 44 24 04             mov    %eax,0x4(%esp)
      401225:       c7 04 24 60 30 40 00    movl   $0x403060,(%esp)
      40122c:       e8 17 00 00 00          call   401248 <_printf>
      

      前 7 条指令是内联调用,常规调用在 5 条指令之后发生。

      【讨论】:

        【解决方案7】:

        这里有个建议,把代码主体写在单独的头文件中。 将头文件包含在必须内联的位置并放入 C 文件的正文中以供其他调用。

        void demo(void)
        {
        #include myBody.h
        }
        
        importantloop
        {
            // code
        #include myBody.h
            // code
        }
        

        【讨论】:

          【解决方案8】:

          我假设你的函数有点小,因为你想内联它,如果是这样,你为什么不把它写在 asm 中?

          至于仅内联对函数的特定调用,我认为没有什么可以为您完成这项任务。一旦一个函数被声明为内联,如果编译器会为你内联它,它会在任何地方看到对该函数的调用。

          【讨论】:

            猜你喜欢
            • 2017-10-24
            • 2014-05-08
            • 2014-03-14
            • 2020-05-03
            • 1970-01-01
            • 2010-09-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多