• #define 的神奇操作


    # define 的神奇操作

    一、宏定义中的 #、## 符号的神奇用法

    1.1 # 的用法

    1.1.1 作用

    #表示字符串化操作符(stringification),其作用是将宏定义中的传入参数名转换成用双引号括起来的参数名字符串

    现在对这句话是不是还不甚理解,没关系,让我们接着往下走。

    1.1.2 举例说明

    #include 
    
    #define ToString(str) #str
    
    int main()
    {
        char str[] = ToString(hello  world); // 等价于 "hello world";
    }

    根据例子,我们再来理解一下#的作用:将宏定义(ToString)中的传入参数名(hello world)转化成用双引号括起来的参数名字符串("hello world")。

    是不是有种恍然大明白的感觉~

    1.1.3 实战

    假设给你一个 JSON 字符串:

    {"name" : "张三"}

    如何以 C 语言的形式输出呢?

    你可以这么做char str[] = "{\"name\":\"张三\"}";,通过使用转义字符\"使得代码可以保存引号"

    既然学习了宏定义中#的作用,那么我们通过#修改一下:

    #define ToString(str)   #str
    int main()
    {
        char str[] = ToString({"name":"张三"});
    }

    好处就是不用添加\,阅读起来更直观。

    1.2 ## 的用法

    1.2.1 作用

    ##表示连接,将宏定义中的一个或多个形参转换成一个实际的参数名

    1.2.2 举例说明

    老规矩,先上代码再解释:

    #include 
    
    #define Conn(x, y) x##y
    
    int main()
    {
        int num1 = 10;
        int n = Conn(num, 1); // n = num1
    
        return 0;
    }

    根据例子,我们再来理解一下##的作用:将宏定义(Conn)中的多个形参(num 和 1)转换成一个实际的参数名(num1)。

    或者你也可以这么写:

    #include 
    
    #define Conn(x) num##x
    
    int main()
    {
        int num1 = 10;
        int n = Conn(1); // n = num1
    
        return 0;
    }

    将宏定义(Conn)中的一个形参(1)转化为实际的参数名(num1)。

    1.2.3 错误用法

    通过上面的举例,你现在是不是已经明白了##的作用了,那么来分析一下下述代码的输出结果:

    #include 
    
    #define Conn(x) num##x
    
    int main()
    {
        int num1 = 1;
        int num2 = 2;
        int num3 = 3;
        int numi = 100;
    
        for (int i = 1; i <= 3; i++)
        {
            int num = Conn(i);
            printf("num = %d\n", num);
        }
    
        return 0;
    }

    期望的输出结果是不是:

    • num = 1
    • num = 2
    • num = 3

    下面让我们看一下实际运行结果:
    image-20221204144244309

    是不是很意外?Conn 宏不是起连接作用吗为什么输出的结果不是预期呢?

    还记得 gcc 的 -E 指令吗,让我们通过 -E 看一下 gcc 预编译的文件:
    image-20221204144449013

    下面让我们来分析一下 main.i 文件内容:

    int main()
    {
        int num1 = 1;
        int num2 = 2;
        int num3 = 3;
        int numi = 100;
    
        for (int i = 1; i <= 3; i++)
        {
            int num = numi;
            printf("num = %d\n", num);
        }
    
        return 0;
    }

    是不是找到问题所在了。原因在于Conn(i)并没有按照我们的预期依次替换为 num1、num2、num3,而是替换为了 numi。

    将宏 Conn(i) 中 i 的值替换为其实际的整数值是不可能的,这是因为宏替换发生在代码编译之前,也就是说宏替换的时候并不知道 i 是一个 int 型的变量,仅仅将其当做一个字符处理。

    解释参考自问题评论:将变量的值传递给C中的宏 - 或代码 (orcode.com)

    二、实际使用

    最近在看 ONVIF 代码的时候,看到一个神奇的操作,下面是简化版本:

    #include 
    
    /* 函数声明 */
    #define DECLARE(x) \
        void Func_##x(int a);
    
    /* 函数定义 */
    #define DEFINE(x) \
        void Func_##x(int a) \
        { \
            printf("a 的值为 %d, %s 型\n", a, #x); \
        }
    
    /* 函数创建 */
    #define CREATE(x) \
        Func_##x
    
    DECLARE(int)
    DEFINE(int)
    
    int main()
    {
        CREATE(int)(10);
    
        return 0;
    }

    在学习了###的用法后,上面的代码就迎刃而解了,转化成普通的代码为:

    #include 
    
    void Func_int(int a);       // 声明函数 --- DECLARE(int)
    void Func_int(int a)        // 定义函数 --- DEFINE(int)
    {
        printf("a 的值为 %d, %s 型\n", a, "int");
    }
    
    int main()
    {
        Func_int(10);           // 使用函数 --- CREATE(int)(10)
    
        return 0;
    }

    参考资料


    __EOF__

  • 本文作者: MElephant
  • 本文链接: https://www.cnblogs.com/hyacinthLJP/p/16949963.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    对象临时中间状态的条件竞争覆盖
    代码随想录算法训练营刷题复习4 :单调栈
    企业计算机服务器中了faust勒索病毒怎么解密,faust勒索病毒解密流程
    oracle 还原被覆盖的视图
    9月10日OpenCV学习笔记——Mask、彩色直方图、人脸检测
    数据库导入现有的mysql文件和_列的别名_和_去重
    idea中java版本设置
    【目标检测】SSD损失函数详解
    games101——作业7
    Efficient Join Order Selection Learning with Graph-based Representation
  • 原文地址:https://www.cnblogs.com/hyacinthLJP/p/16949963.html