C进阶养成记 - 养成记25:#和##操作符分析

--事物的难度远远低于对事物的恐惧!

    这个篇章我们来谈一谈#和##操作符,可能很多人都没注意这两个操作符,然而这两个操作符,往往能给我们开发提供很大的便利。

    #运算符:

        -#运算符用于在预处理期将宏参数转换为字符串

        -#的转换作用是在预处理期完成的,因此只在宏定义中有效

        -编译器不知道#的转换作用

        用法如下:#define STRING(x) #x

    下边来看看代码

#define STRING(x) #x
int main()
{
    char *p = STRING(abc);
	
    return 0;
}

预编译后的文件内容如下,我们可以看到,我们所定义的宏STRING(abc),在经过预处理器处理后,成为了"abc"字符串。

#line 1 "main.c"

int main()
{
    char *p = "abc";
    
    return 0;
}

知道了#运算符的基本用法,我们来看看#运算符在工程中比较巧妙的使用方式:

#include <stdio.h>

//通过#及逗号表达式,使得我们使用宏的时候,既能打印出调用的函数名,又能执行函数
#define CALL(f, p) (printf("Call function %s\n", #f), f(p))
   
int square(int n)
{
    return n * n;
}

int func(int x)
{
    return x;
}

int main()
{
    int result = 0;
    
    result = CALL(square, 4);
    
    printf("result = %d\n", result);
    
    result = CALL(func, 10);
    
    printf("result = %d\n", result);

    return 0;
}

编译执行结果如下,是不是很巧妙:



##运算符:

    -##运算符用于在预处理期粘连两个标识符

    -##的连接作用是在预处理期完成的,因此只在宏定义中有效

    -编译器不知道##的连接作用

    用法:#define CONNECT(a,b) a##b

    来看一个例子:

#define NAME(n) name##n

int main()
{
    int NAME(1);
    int NAME(2);
    
    NAME(1) = 1;
    NAME(2) = 2;
	
    return 0;
}

使用预处理器处理后的文件内容为:

#line 1 "main.c"

int main()
{
    int name1;
    int name2;
    
    name1 = 1;
    name2 = 2;
	
    return 0;
}

从预处理文件看出,##运算符已经把name与n粘结在一起了。

下边再来看看工程中典型的用法:

#define STRUCT(type) typedef struct _tag_##type type;\
                     struct _tag_##type

STRUCT(Student)    //使用宏STRUCT来定义名为Student的结构体,后边可直接使用Student声明定义结构体变量
{
    char* name;
    int id;
};

int main()
{
    
    Student s1;
    Student s2;
    
    s1.name = "s1";
    s1.id = 0;
    
    s2.name = "s2";
    s2.id = 1;

    return 0;
}

我们用预处理器来处理下,得到的文件内容如下:

#line 1 "main.c"

typedef struct _tag_Student Student; 
struct _tag_Student
{
    char* name;
    int id;
};

int main()
{
    
    Student s1;
    Student s2;
    
    s1.name = "s1";
    s1.id = 0;
    
    s2.name = "s2";
    s2.id = 1;
    
    return 0;
}

这样看,是不是很明了了呢?再定义一个结构体也很方便,直接STRUCT(structName)即可。

总结:

    -#运算符用于在预处理期将宏参数转换为字符串

    -##运算符用于在预处理期粘连两个标识符

    -编译器不知道#和##运算符的存在

    -#和##运算符只在宏定义中有效

猜你喜欢

转载自blog.csdn.net/lms1008611/article/details/80143630