【为什么】C++中的宏

简介: 【为什么】C++中的宏

C++宏的简单理解

C++中的宏是一种代码片段,可以被宏的值所替换。宏是用#define指令来定义的,用#undef指令来取消定义。宏有两种类型:类对象宏和类函数宏。

类对象宏就像一个常量,只不过不需要分配内存空间。

类函数宏就像一个函数,只不过不需要调用和返回。

大白话来说,宏就是一种让你写代码时更方便、更简洁、更灵活的工具

举例说明

假设你想定义一个宏,用来计算两个数的最大值。你可以这样写:

#define MAX(a,b) ((a)>(b)?(a):(b))

这就是一个类函数宏,它接受两个参数a和b,然后用三目运算符返回较大的那个。

当你在程序中使用这个宏时,比如写:

int x = MAX(3,5);

编译器会在编译之前把宏名替换成宏的定义,也就是:

int x = ((3)>(5)?(3):(5));

这样就可以得到x的值为5

类对象宏和类函数宏

    • 类对象宏是一个标识符,被一段代码片段所替换。它看起来像一个数据对象,比如一个常量。

    例如,你可以这样定义一个类对象宏:

    #define PI 3.14159

    这样在程序中使用PI时,就相当于使用3.14159。

      • 类函数宏是一个标识符,后面跟着一对括号和一些参数,被一段代码片段所替换。它看起来像一个函数调用,但实际上不是。

      例如,你可以这样定义一个类函数宏:

      #define SQUARE(x) ((x)*(x))

      这样在程序中使用SQUARE(5)时,就相当于使用((5)*(5))。

      什么时候用宏呢

      宏的使用场景一般是:

      当你需要在编译时替换一些常量或表达式,而不是在运行时计算它们。

      当你需要定义一些简单的操作,而不想为它们创建函数或模板。

      当你需要根据不同的编译选项生成不同的代码,比如调试信息或条件编译。

      当你需要使用一些特殊的语法或符号,比如变长参数或字符串化。

      但是宏也有很多局限和风险,所以应该谨慎使用,并尽量用C++提供的其他特性来代替它们

      那么C++提供了哪些特性可以代替宏呢?

      C++提供了一些特性可以代替宏,比如:

      用inline函数或模板来代替函数式宏,可以保证类型安全和编译优化。

      用constexpr或const来代替常量宏,可以避免重复定义和命名冲突。

      用using或typedef来代替类型别名宏,可以增加可读性和灵活性。

      用enum class或namespace来代替枚举宏,可以提供作用域和强类型检查。

      用if constexpr或模板特化来代替条件编译宏,可以实现编译时分支选择。

      用字符串字面量或运算符重载来代替字符串化或连接宏,可以实现更自然的语法。

      还有哪些情况是必须使用宏的呢?

      宏在C++中的必要性已经大大降低了,但是还有一些情况是无法用其他特性替代的,比如:

      当你需要定义一些编译器或平台相关的常量或标志,比如__FILE__或_WIN32。

      当你需要使用一些预处理指令,比如#include或#pragma。

      当你需要实现一些复杂的元编程技巧,比如BOOST_PP库。

      宏和内联函数的区别

      宏和内联函数有以下几个区别:

      宏是由预处理器展开的,而内联函数是由编译器复制到调用处的。

      宏必须在程序开始处定义,而内联函数可以在程序任何地方声明。

      宏不能访问类的数据成员,而内联函数可以。

      宏没有类型检查和返回类型,而内联函数有。

      宏可能会导致副作用和错误,而内联函数不会。

      代码示例

      以下是宏和内联函数的一些代码示例:

      // 定义一个宏,用于计算两个数的最大值 
      #define MAX(a,b) ((a) > (b) ? (a) : (b))
      // 定义一个内联函数,用于计算两个数的最大值 
      inline int max(int a, int b) { return a > b ? a : b; }
      // 调用宏和内联函数 
      int x = 10; int y = 20; int z = MAX(x++, y++); 
      // z = 21, x = 11, y = 22 
      int w = max(x++, y++); 
      // w = 22, x = 12, y = 23

      image.gif

      可以看到,宏会导致x和y被增加两次,而内联函数不会。这是因为宏只是简单地替换文本,而不考虑副作用

      为什么宏会导致x和y被增加两次?

      这是因为在调用宏的时候,宏的参数会被直接替换到宏的定义中,而不会像函数一样先计算参数的值。所以,当你调用MAX(x++, y++)时,实际上相当于执行了以下代码:

      ((x++) > (y++) ? (x++) : (y++))

      这样就导致了x和y被增加了两次。如果你想避免这种情况,你可以先把x和y的值存储在临时变量中,然后再调用宏。或者你可以直接使用内联函数,因为内联函数会先计算参数的值,然后再复制到函数体中。

      详细解释:

      首先,你定义了一个宏MAX,用于计算两个数的最大值。然后,你定义了两个变量x和y,分别赋值为10和20。接着,你调用了宏MAX(x++, y++),并把结果赋值给变量z。这时候,预处理器会把宏的参数x++和y++直接替换到宏的定义中,得到以下代码:

      ((x++) > (y++) ? (x++) : (y++))

      这段代码是一个三元运算符,它的意思是如果x++大于y++,就返回x++的值,否则返回y++的值。注意这里的x++和y++都是后置自增运算符,它们会先返回原来的值,然后再增加1。所以我们可以把这段代码拆分成以下几步:

      1.比较x++和y++的值。因为x和y分别是10和20,所以x++返回10,y++返回20。比较结果是false。

      2.因为比较结果是false,所以执行三元运算符的第三部分:(y++)。这时候y已经被增加了1,变成了21。所以(y++)返回21,并且再次增加1。

      3.把(y++)的结果赋值给z。所以z等于21。

      4.因为执行完了三元运算符的第三部分:(y++),所以跳过第二部分:(x++)。但是这时候x已经被增加了1,变成了11。

      5.代码执行完毕。

      最终结果是z等于21,x等于11,y等于22(被增加两次)。

      相关文章
      |
      6月前
      |
      编译器 Linux C++
      【C++ 跨平台开发 】掌握 C++ 跨平台关键宏的使用
      【C++ 跨平台开发 】掌握 C++ 跨平台关键宏的使用
      132 3
      |
      6月前
      |
      存储 缓存 安全
      【cmake 生成配置文件】CMake与现代C++:配置文件宏的深度探索与应用
      【cmake 生成配置文件】CMake与现代C++:配置文件宏的深度探索与应用
      255 0
      |
      6月前
      |
      安全 编译器 C语言
      【C++ 编译器 版本支持】深度解读C++ 版本以及编译器版本相关宏
      【C++ 编译器 版本支持】深度解读C++ 版本以及编译器版本相关宏
      126 0
      |
      编译器 Android开发 C++
      [√]build.gradle,mk,c++预处理宏联动关系
      [√]build.gradle,mk,c++预处理宏联动关系
      80 0
      |
      C++
      C++宏 #与##的区别
      C++宏 #与##的区别
      54 0
      |
      JSON C语言 数据格式
      【C/C++】防止不必要的局部宏替换
      如何避免和防止宏定义在不必要的位置进行替换
      225 0
      |
      编译器 C语言 C++
      C/C++,不废话的宏使用技巧
      C/C++,不废话的宏使用技巧
      147 0
      |
      存储 自然语言处理 编译器
      C/C++:程序环境和预处理/宏
      简单讲解了宏定义、预处理、条件编译等等
      C/C++:程序环境和预处理/宏
      |
      C++
      C++ 你会使用cmath库里的宏常量吗?(π、e、ln2、√2、(2/√π) 等等)
      C++ 你会使用cmath库里的宏常量吗?(π、e、ln2、√2、(2/√π) 等等)
      268 0