#define 的神奇操作

# define 的神奇操作

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

1.1 # 的用法

1.1.1 作用

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

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

1.1.2 举例说明

#include <stdio.h>
#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 <stdio.h>
#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 <stdio.h>
#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 <stdio.h>
#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 <stdio.h>
/* 函数声明 */
#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 <stdio.h>
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;
}

参考资料

  • #define宏定义中的#,##,@#,\ 这些符号的神奇用法 – 简书 (jianshu.com)
  • C语言宏#define中#,##,#@和\的用法_yishizuofei的博客-CSDN博客
  • #define用法集锦(非常全)_king110108的博客-CSDN博客_#define (…)
© 版权声明
好牛新坐标
版权声明:
1、IT大王遵守相关法律法规,由于本站资源全部来源于网络程序/投稿,故资源量太大无法一一准确核实资源侵权的真实性;
2、出于传递信息之目的,故IT大王可能会误刊发损害或影响您的合法权益,请您积极与我们联系处理(所有内容不代表本站观点与立场);
3、因时间、精力有限,我们无法一一核实每一条消息的真实性,但我们会在发布之前尽最大努力来核实这些信息;
4、无论出于何种目的要求本站删除内容,您均需要提供根据国家版权局发布的示范格式
《要求删除或断开链接侵权网络内容的通知》:https://itdw.cn/ziliao/sfgs.pdf,
国家知识产权局《要求删除或断开链接侵权网络内容的通知》填写说明: http://www.ncac.gov.cn/chinacopyright/contents/12227/342400.shtml
未按照国家知识产权局格式通知一律不予处理;请按照此通知格式填写发至本站的邮箱 wl6@163.com

相关文章