C编程:内联函数VS宏

时间:2015-05-18 09:44:29

标签: c

运行以下代码:

案例1:

 #include <stdio.h>

int count=0;

    void g(void){
    printf("Called g, count=%d.\n",count);
}

#define EXEC_BUMP(func) (func(),++count)
typedef void(*exec_func)(void); 
inline void exec_bump(exec_func f){
    f();
    ++count;
}

int main(void)
{
    //int count=0;
     while(count++<10){
        EXEC_BUMP(g); 
        //exec_bump(g); 
     }
     return 0;
}

案例2:

#include <stdio.h>

int count=0;

void g(void){
     printf("Called g, count=%d.\n",count);
}

#define EXEC_BUMP(func) (func(),++count)
typedef void(*exec_func)(void); 
inline void exec_bump(exec_func f){
    f();
    ++count;
}

int main(void)
{
    //int count=0;
    while(count++<10){
        //EXEC_BUMP(g);
        exec_bump(g); 
    }
    return 0;
 }

案例3:

#include <stdio.h>

int count=0;

void g(void){
     printf("Called g, count=%d.\n",count);
}

#define EXEC_BUMP(func) (func(),++count)
typedef void(*exec_func)(void);
inline void exec_bump(exec_func f){
      f();
     ++count;
}

int main(void)
{
    int count=0;
    while(count++<10){
        //EXEC_BUMP(g); 
        exec_bump(g); 
     }
     return 0;
 }

案例4:

#include <stdio.h>

int count=0;

void g(void){
    printf("Called g, count=%d.\n",count);
}

#define EXEC_BUMP(func) (func(),++count)
typedef void(*exec_func)(void); 
inline void exec_bump(exec_func f){
    f();
    ++count;
}

int main(void)
{
    int count=0;
     while(count++<10){
      EXEC_BUMP(g);
      //exec_bump(g);
     }
     return 0;
 }

案例之间的差异是否定义局部变量,并使用内联函数与宏。 为什么上面的代码给出不同的输出? 此外,是否有人可以让我知道为什么使用内联函数比宏更有效。

以下输出:

Case 1:
    Called g, count=1.
    Called g, count=3.
    Called g, count=5.
    Called g, count=7.
    Called g, count=9.

Case 2:
    Called g, count=1.
    Called g, count=3.
    Called g, count=5.
    Called g, count=7.
    Called g, count=9.


Case 3:
    Called g, count=0.
    Called g, count=1.
    Called g, count=2.
    Called g, count=3.
    Called g, count=4.
    Called g, count=5.
    Called g, count=6.
    Called g, count=7.
    Called g, count=8.
    Called g, count=9.

Case 4:
    Called g, count=0.
    Called g, count=0.
    Called g, count=0.
    Called g, count=0.
    Called g, count=0.

2 个答案:

答案 0 :(得分:3)

我认为你的测试有点比较苹果和橘子,尤其是案例3和4.你的宏正在递增本地count变量,你的内联函数正在递增全局count变量。 / p>

您将它们命名为count,但宏会增加本地范围内的宏。这是使用宏时必须注意的事项,因为它们没有范围概念。

我建议将全局变量命名为与本地计数器不同,以避免混淆。

更新了一些要求的详细信息:

使用宏,它们有点像蛮力'复制和粘贴'代码生成机制,带有一些文本替换。所以当你定义一个像:

的宏
#define FOO (++count)

...并调用它,就像将++count字面写入您调用它的函数中一样。宏扩展总是强有力地在这个意义上内联代码,并且它在编译器和链接器甚至到达它之前就这样做了。预处理器在构建过程中是一个完全独立的阶段,因此宏不遵循内联函数的作用域规则。由于这个原因,我们也倾向于对它们更加小心,因为遇到类似测试的情况可能会让调试变得非常混乱。

另一方面,函数内联实际上是编译时(有时甚至是链接时)优化。生成的目标代码甚至最终二进制文件都没有正常的函数调用开销,将事物推送到堆栈,可能必须通过精确的寄存器传递特定的东西(取决于调用约定)等等。所以你可以得到一个类似的作为宏扩展的一种性能优势,但内联函数尊重语言的范围规则。

值得注意的是,inline更像是一个暗示,而一些编译器只是直接忽略它并仅将其视为内部链接的说明符。如果您或优化器选择 not 来内联某些东西,实际上可以在现实世界的场景中获得更快的东西,因为它可能有助于减少指令缓存未命中,或者帮助优化器分配寄存器以获得更多常见的案例执行分支(有时程序间优化实际上会干扰您将代码缩减到平坦的竞争环境,当您可能希望优化更偏向于代码的常见情况分支时,过度内联实际上似乎是干扰这一点,并给出更糟糕的结果)。

宏并没有为编译器或链接器选择性地内联提供这样的奢侈,但它们是一种非常不同的代码生成工具。宏可以用于生成新函数,例如,内联函数不能。

答案 1 :(得分:2)

当您想要考虑内联函数时,一个良好的开端可能是令人惊讶的忽略内联。

函数的语义(含义)在内联声明时不会改变一位。它只是编译器优化实现细节的一个提示,并且编译器不会被迫遵守它。另一方面,编译器可以自由地内联未声明为内联的函数,并且大多数现代编译器在使用优化选项执行时都会执行此操作。

另一方面,预处理器宏的含义很简单而且很愚蠢。它是纯文本替换,在翻译阶段之前发生。生成的C程序文本中出现的所有标识符表示它们在代码中该位置的含义。