C/C++编程中的宏与内联函数

宏与内联函数都可提高代码的效率,但在实现和行为上有显著差异。

宏:预处理器指令,使用 #define 进行定义。它们在编译之前由预处理器进行文本替换。

特点:

  1. 文本替换:宏在编译前进行文本替换,没有类型检查或错误检查。
  2. 无类型安全:由于宏仅是文本替换,所以不进行类型检查。
  3. 避免函数调用开销:宏可以减少函数调用的开销,但如果宏体较大,可能会导致代码膨胀。
  4. 调试困难:宏在编译前就被替换,所以在调试时可能会带来困难。

 所以一般定义宏或者宏函数的代码量都很少,很小,基本上是一些简单操作。

示例1:平方运算
#define SQUARE(x) ((x) * (x))
示例2:cuda的错误检查函数
#define CHECK(call)                                   \
do                                                    \
{                                                     \
    const cudaError_t error_code = call;              \
    if (error_code != cudaSuccess)                    \
    {                                                 \
        printf("CUDA Error:\n");                      \
        printf("    File:       %s\n", __FILE__);     \
        printf("    Line:       %d\n", __LINE__);     \
        printf("    Error code: %d\n", error_code);   \
        printf("    Error text: %s\n",                \
            cudaGetErrorString(error_code));          \
        exit(1);                                      \
    }                                                 \
} while (0)

//调用宏
if(){
    CHECK(call)
}
else{

}

//等价于:
if(){
    do                                                    \
    {                                                     \
        const cudaError_t error_code = call;              \
        if (error_code != cudaSuccess)                    \
        {                                                 \
            printf("CUDA Error:\n");                      \
            printf("    File:       %s\n", __FILE__);     \
            printf("    Line:       %d\n", __LINE__);     \
            printf("    Error code: %d\n", error_code);   \
            printf("    Error text: %s\n",                \
            cudaGetErrorString(error_code));          \
            exit(1);                                      \
        }                                                 \
    } while (0)
}
else{

}

//如果像定义函数一样加 { },会造成if语句中有两个{},虽然能运行,但可读性差并且容易生成错误

可以看到宏的定义不包含参数数据类型,宏仅是文本替换,不进行参数替换。

第二个示例中do {...}whlie(0)是宏定义的内容,可以认为是一个函数,这里do{...}while中的内容才是宏的主体,也就是函数内容。do { ... } while (0) 是一个循环结构,但在这里它只执行一次。这种结构在宏定义中常用于确保宏在任何地方都能安全地使用,特别是在条件语句中。

那么宏函数的定义为什么不用{}呢,首先宏#define CHECK(x),要确保后面是一个单独的语句,如果在if,else使用宏时会导致else与if的错误匹配。do { ... } while (0) 结构提供了一种在宏定义中创建复杂逻辑的安全和一致的方式。所以在这里定义简单的宏只要使用单句即可,比如示例1,如果需要生成多行,最好使用do{}while(0)来确保宏定义使用的安全。

内联函数(Inline Function)

内联函数是一种通知编译器尝试在调用点展开函数体的方法,以减少函数调用的开销。使用 inline 关键字定义。

特点

  1. 类型检查:与普通函数一样,内联函数进行类型检查和错误检查。
  2. 编译器优化:内联是一种请求,编译器可以选择忽略这个请求。如果函数体较大或者包含复杂的控制结构,编译器可能不会进行内联。
  3. 减少函数调用开销:如果编译器接受内联请求,它会在调用点展开函数体,减少函数调用的开销。
  4. 可能的代码膨胀:如果内联函数在多个地方被调用,可能会导致代码膨胀。
inline int square(int x) {
    return x * x;
}

宏函数 vs 内联函数

  • 类型安全:内联函数比宏函数更安全,因为它们进行类型检查。
  • 调试:内联函数比宏更易于调试,因为它们是实际的函数。
  • 控制:编译器可以选择不内联一个内联函数,但宏总是进行文本替换。
  • 使用场景:宏功能更强大,可以包含任何东西(如多个语句、控制结构等),而内联函数受到常规函数的限制。

在C++中,在类的内部定义了函数体的函数,被默认为是内联函数。而不管你是否有inline关键字。当然在类的外部定义类的成员函数加上inline可以达到同样的效果。

 小结,内联函数与宏函数都可在使用处展开,可以避免函数调用的开销,宏函数是由预处理机制展开,不会检查类型安全,所以在传入错误参数时会出问题,而内联函数是一个函数,是由编译器决定是否内联,会对传入的参数进行类型检查。

你可能感兴趣的:(c/c++,c语言,c++)