C++基础知识--从宏定义变量名看相关展开顺序

TIME_STAT_1TIME_STAT_2 两个宏的目的是想定义一个不重复的变量名(根据行号),但是只有 TIME_STAT_1 能够达到目的

以下是 TIME_STAT_1TIME_STAT_2 两种宏展开的详细流程解析,配合图示说明关键差异:


1. 宏定义说明

#define CONCAT(x, y) x##y
#define TIME_STAT_LOGIC_1(node, num) TimeRecorder CONCAT(node, num)(#node);
#define TIME_STAT_1(node) TIME_STAT_LOGIC_1(node, __LINE__);

#define TIME_STAT_LOGIC_2(node, num) TimeRecorder node##num(#node);
#define TIME_STAT_2(node) TIME_STAT_LOGIC_2(node, __LINE__);

2. 展开流程对比

场景假设
  • 调用位置:源文件第 42
  • 调用方式:TIME_STAT_1(foo)TIME_STAT_2(bar)

3. TIME_STAT_1 展开流程

graph TD
    A[TIME_STAT_1(foo)] --> B[展开外层宏 TIME_STAT_LOGIC_1(foo, __LINE__)]
    B --> C[参数替换: node=foo, num=42 (__LINE__先展开)]
    C --> D[展开CONCAT宏: CONCAT(foo, 42)]
    D --> E[拼接符号: foo##42 → foo42]
    E --> F[最终展开: TimeRecorder foo42("foo");]

分步解析

  1. 第一层展开
    TIME_STAT_1(foo)TIME_STAT_LOGIC_1(foo, __LINE__)
    
  2. 参数求值
    • __LINE__ 先被展开为 42
    • 此时变为 TIME_STAT_LOGIC_1(foo, 42)
  3. 第二层展开
    TimeRecorder CONCAT(foo, 42)(#foo);
    
  4. CONCAT 处理
    CONCAT(foo, 42) → foo##42 → foo42
    
  5. 字符串化
    #foo "foo"
    
  6. 最终结果
    TimeRecorder foo42("foo");
    

4. TIME_STAT_2 展开流程

graph TD
    A[TIME_STAT_2(bar)] --> B[展开外层宏 TIME_STAT_LOGIC_2(bar, __LINE__)]
    B --> C[直接拼接符号: bar##__LINE__]
    C --> D[生成中间符号: bar__LINE__]
    D --> E[__LINE__未展开,保留字面量]
    E --> F[最终展开: TimeRecorder bar__LINE__("bar");]

分步解析

  1. 第一层展开
    TIME_STAT_2(bar)TIME_STAT_LOGIC_2(bar, __LINE__)
    
  2. ## 操作符优先处理
    • 直接拼接 bar__LINE__ 字面量
    • 生成 bar__LINE__(此时 __LINE__ 未被展开)
  3. 字符串化
    #bar "bar"
    
  4. 最终结果
    TimeRecorder bar__LINE__("bar");  // 行号未展开!
    

5. 关键差异图示

# 预处理结果对比(假设调用位置为第42行)

+---------------------+-------------------------------+
|     TIME_STAT_1     |         TIME_STAT_2           |
+---------------------+-------------------------------+
| TimeRecorder foo42  | TimeRecorder bar__LINE__      |
| ("foo");           | ("bar");                      |
+---------------------+-------------------------------+
| __LINE__先展开      | __LINE__未展开                |
| 正确生成唯一标识符  | 生成错误字面量                |
+---------------------+-------------------------------+

6. 根本原因分析

阶段 TIME_STAT_1 TIME_STAT_2
参数展开时机 先展开 __LINE__ 再拼接 先执行 ## 拼接,阻止展开
标准依据 参数优先展开规则 ## 操作符优先级更高
调试技巧 gcc -E 可见分步展开过程 预处理结果直接显示 __LINE__

总结

  • TIME_STAT_1:安全可靠,符合预期
    (先展开参数 → 再拼接 → 生成正确符号)
  • TIME_STAT_2:存在设计缺陷
    (直接拼接 → 阻止参数展开 → 生成错误结果)

实际开发中应优先选择 TIME_STAT_1 的方式


【技术人的鼓励】❤️ 如果这篇文章对您有帮助,欢迎点击打赏按钮支持博主!您的鼓励是我持续输出优质技术内容的动力,哪怕只是1元也足以让我感受到这份珍贵的认可。

你可能感兴趣的:(c++,基础知识)