C语言“#”用法总结

0 前言

“#”在C语言中有多种用法,总结有5种:

  1. “#”用作引用头文件
  2. “#”用作宏定义
  3. “#”用作预编译时的警告、错误
  4. “#”用作将宏定义转换成字符串
  5. “##”用作连接两个标识符

以下分别详细解释。

1 “#”用作引用头文件

这一作用比较常见,如包含头文件时,使用 #include;

2 “#”用作宏定义

#define, #endif, #ifndef

3 “#”用作预编译时的警告、错误

#warning, #error

4 “#”用作将输入转换成字符串

总所周知,宏定义是可以定义字符串的,如下:

#include <stdio.h>

#define STR                 "test macro to string"

int main(int argc, char** argv)
{
    printf("%srn",STR);
    return 0;
}

打印如下:

test macro to string

但是在某些场景下,无法直接宏定义字符串,需要宏定义转成字符串。目前有两个应用场景,如有其他应用场景,欢迎大家评论区留言。

4.1 直接将输入转换成字符串

比如,将数字以字符串的形式打印,或者将任意输入以字符串形式打印:
用宏定义 #define STR1(a) #a 实现:

#include <stdio.h>

#define STR1(a)             #a	//以“#”的形式将输入转换成字符串,必须以宏定义的方式

int main(int argc, char** argv)
{
    printf("print string: %srn", STR1(test string));
    printf("print string: %srn", STR1(10086));
    //printf("print string: %srn", #10086);//必须用上面宏定义的方式,直接加#编译报错!!!
    
    return 0;
}

输出:

test macro to string
print string: test string
print string: 10086

代码第3行,用宏定义的方式将输入转换成字符串,注意,必须用宏定义的方式,直接加#编译报错!

4.2 间接将输入转换成字符串

请大家思考一个问题,上节中,如果将数字10086和字符传test string 定义成宏的形式,然后再用STR(a)去打印会如何?直接上代码:

#include <stdio.h>

#define STR1(a)             #a      //以“#”的形式将输入转换成字符串,必须以宏定义的方式
#define TEST_NUM            10086
#define TEST_STR            "test string"

int main(int argc, char** argv)
{
    printf("%d to string: %srn", TEST_NUM, STR1(TEST_NUM));
    printf("%s to string: %srn", TEST_STR, STR1(TEST_STR));
    
    return 0;
}

打印如下:

10086 to string: TEST_NUM
test string to string: TEST_STR

由上面结果可见,如果我用宏定义了一个整数或字符串,然后用4.1小节的方式打印宏定义,只能直接将宏定义的名字打出,而不能打印出宏定义定义的内容。我们想要实现将打印“TEST_NUM”就能打印出“10086”字符串的效果,该如何实现呢?
还是看代码:(注意第6行,11、12行)

#include <stdio.h>

#define STR1(a)             #a      //以“#”的形式将输入转换成字符串,必须以宏定义的方式
#define TEST_NUM            10086
#define TEST_STR            "test string"
#define STR2(a)             STR1(a) //用二级字符串对的方式实现转换成字符串

int main(int argc, char** argv)
{
    printf("%d to string: %srn", TEST_NUM, STR1(TEST_NUM));
    printf("%s to string: %srn", TEST_STR, STR1(TEST_STR));
    printf("%d to string: %srn", TEST_NUM, STR2(TEST_NUM));
    printf("%s to string: %srn", TEST_STR, STR2(TEST_STR));
    
    return 0;
}

运行结果打印如下:

10086 to string: TEST_NUM
test string to string: TEST_STR
10086 to string: 10086
test string to string: "test string"

上述结果实现了我们的预期结果(见11、12行打印)。我们 用第6行实现了该功能
注意:宏定义是完全替换,所以第12行的打印将引号也打印了出来。如果第4行宏定义的10086加上括号,同样第11行也会将括号打印出来。

5 “##”用作连接两个标识符

“##”可以用于将两个标识符连接起来,上代码:

#include <stdio.h>

#define CONNECT(a,b)            a##b
#define NAME_1                  "zhangsan"
#define NAME_2                  "lisi"
#define NAME(name)              NAME_##name
int main(int argc, char** argv)
{
    printf("num: %drn", CONNECT(11,12));
    printf("name: %srn", NAME(1));
    printf("name: %srn", NAME(2));

    return 0;
}

结果打印如下:

num: 1112
name: zhangsan
name: lisi

代码第3行宏定义将两个标识符连接,第9行打印如结果第1行所示,将11和12连接起来变成1112;
第10行和第11行,宏展开后,NAME(1)变成NAME_##1NAME(2)变成NAME_##2,由于##的作用是连接作用,所以NAME(1)就是NAME_1,打印出来就是“zhangsan”;同样NAME(2)就是NAME_2,打印出来就是“lisi”

总结

  1. “#”用作引用头文件:#include "xxx.h"
  2. “#”用作宏定义:#define, #endif, #ifndef
  3. “#”用作预编译时的警告、错误:#warning, #error
  4. “#”用作将宏定义转换成字符串:#define STR(a) #a
  5. “##”用作连接两个标识符:#defind CONECT(a,b) a##b

欢迎大家评论区留言交流!