C语言中#和##的作用

  1. C语言中#和##的作用
  2. 提供代码都是可以正常运行起来

###符号是C和C++的宏(macro)属于编译器预处理的范畴,编译期间完成

  1. #的功能是将其后面的宏参数进行字符串化操作;#符号是把传递过来的参数多做字符串进行处理;简单说就是在对它所引用宏参数在其左右加一个双引号,示例代码如下:
1
2
3
4
5
6
7
8
9
#include <stdio.h>

#define str(param) #param //#将param参数经行字符串化

int main()
{
printf(str(hello 122 333444\n));
return 0;
}

输出结果:

1
hello 122 333444

  1. ##符号称为连接符,将两个或多个标记连接为一个合法的标识符;代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
/* ## */
#define var(x) var##x
int main()
{
int var(1) = 1;
int var(2) = 2;
int var(3) = 3;

printf("var2 : %d\n", var2);
return 0;
}

运行结果:

1
var2 : 2
  1. 比如要做一个菜单项由变量和函数指针组成的结构体数组,并且希望在函数名和菜单项命名之间有直观的名字上的关系,那么下面的代码就非常实用:可能在有些规则编码中是不允许使用的,因为这样,没有明确的关系,难以查找。这里只是总结一下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <stdio.h>

#define barrier(x) barrier##x
#define COMMAND(NAME) {NAME , NAME##_command}

/**/
struct command
{
char *name;
void(*function)(void);
};

/**
* @brief 建议在两个需要都被打印的数组中添加一个变量,比如下面所示;
* 或者可以指定printf打印字符串长度,至于原因,自己想想
*/
char quit[4] = {"quit"};
char barrier(0) = 0; /*阻隔*/
char help[4] = {"help"};
void quit_command(void);
void help_command(void);

/*在这里已经对变量赋值和函数指针赋值,展开效果:
* struct command commands[] = {{quit quit_command},{help help_command}};
*/
struct command commands[] = { COMMAND(quit) ,COMMAND(help)};

void quit_command(void)
{
printf("quit_command name: %-4.4s\n", commands[0].name);
/*-4.4s:关于这个可以在本博客中搜索 "printf打印指定长度字符串" */
}

void help_command(void)
{
printf("help_command name: %s\n", commands[1].name);
}

int main()
{
commands[0].function();
commands[1].function();
return 0;
}

运行结果:

1
2
quit_command name: quit
help_command name: help

COMMAND宏在这里充当一个代码生成器的作用,这样可以在一定程度上减少代码密度,间接地也可以减少不留心造成地错误,

  1. 我们还可以n个##连接n+1个字符串,这个也是#符号不具备地,比如:
1
2
3
4
5
6
#define LINK_MULT(a,b,c,d)				a##_##b##_##c##_##d
typedef struct _record_type LINK_MULT(name,Company,Position,salay);
/*
* 语句展开效果:
* typedef struct _record_type typedef struct _record_type
*/
  1. ##功能是在带参的宏定义中,将两个字符串连接起来,形成一个新的子串;

假设程序中已经定义这样一个带参的宏

1
2
3
4
5
6
7
8
9
#define poster(n)		printf("t"#n"=%d\n",t##n);
/*同时又定义了一个整形变量*/
int t9 = 9;
int main()
{
/*在编译时,poster(9)扩展为 printf("t""9""=%d\n",t9);*/
poster(9);
return 0;
}

运行结果:

1
t9=9

注意在这个例子中,
上面的是一般用法

  1. 当宏参数是另一个宏的时候,需要注意的是凡宏定义里有用#和##的地方,宏参数是不会再展开 非###`的情况:
1
2
3
4
5
#define TOW					(2)
#define MUL(a,b) (a*b)
printf("%d * %d = %d\n",TOW,TOW,MUL(TOW,TOW));
这行的宏展开为:
printf("%d * %d = %d\n",2,2,MUL(2,2));
  1. 当有###的时候
1
2
3
4
5
6
7
#define A 			2
#define STR(s) #s
#define CONS(a,b) (int)(a##e##b)
printf("int max:%s\n", STR(INTPTR_MAX)); //INTPTR_MAX =》stdint.h
//展开 printf("int max:%s\n", "INTPTR_MAX"); //INTPTR_MAX =》stdint.h
printf("%s\n",CONS(A,A)); //COMPILE ERROR
//展开 printf("%s\n","AeA");

INTPTR_MAX和A都不会展开然而解决这个问题方法很多,加一层中间转换宏,加这层宏的用意是把所有宏的参数在这层里全部展开,那么在转换宏里的那一个宏就能得到正确的宏参数。

1
2
3
4
5
6
7
8
#define A 			2
#define _STR(s) #s
#define STR(s) _STR(s) //转换宏
#define _CONS(a,b) (int)(a##e##b)
#define CONS(a,b) _CONS(a,b) //转换宏

printf("int max:%s\n", STR(INTPTR_MAX)); //INTPTR_MAX =》stdint.h
printf("CONS: %d\n",CONS(A,A)); //COMPILE ERROR

输出结果:

1
2
int max:2147483647i32
CONS: 200

  1. ###的一些应用,合并匿名创建变量名
1
2
3
4
5
#define _ANONYMOUS1(type,var ,line)			type var##line
#define _ANONYMOUS0(type,line) _ANONYMOUS1(type,anonymous ,line)
#define ANONYMOUS0(type) _ANONYMOUS0(type,__LINE__)

ANONYMOUS0(static int) = 100;
  1. 填充结构体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <stdio.h>

#define barrier(x) barrier##x
#define COMMAND(NAME) {NAME , NAME##_command}

/**/
struct command
{
char *name;
void(*function)(void);
};

/**
* @brief 建议在两个需要都被打印的数组中添加一个变量,比如下面所示;
* 或者可以指定printf打印字符串长度,至于原因,自己想想
*/
char quit[4] = {"quit"};
char barrier(0) = 0; /*阻隔*/
char help[4] = {"help"};
void quit_command(void);
void help_command(void);

/*在这里已经对变量赋值和函数指针赋值,展开效果:
* struct command commands[] = {{quit quit_command},{help help_command}};
*/
struct command commands[] = { COMMAND(quit) ,COMMAND(help)};

void quit_command(void)
{
printf("quit_command name: %-4.4s\n", commands[0].name);
/*-4.4s:关于这个可以在本博客中搜索 "printf打印指定长度字符串" */
}

void help_command(void)
{
printf("help_command name: %s\n", commands[1].name);
}

int main()
{
commands[0].function();
commands[1].function();
return 0;
}

运行结果:

1
2
quit_command name: quit
help_command name: help
  1. 记录文件名
1
2
3
4
#define _GET_FILE_NAME(f)					#f
#define GET_FILE_NAME(f) _GET_FILE_NAME(f)
char file_name[] = { GET_FILE_NAME(__FILE__)};
printf("file_name %s\n", file_name);
  1. 得到一个数值类型所对应的字符串缓冲大小
1
2
3
4
#define _TYPE_BUF_SIZE(type)		sizeof(#type)
#define TYPE_BUF_SIZE(type) _TYPE_BUF_SIZE(type)
char buf[TYPE_BUF_SIZE(INT32_MAX)] = { GET_FILE_NAME (INT32_MAX) };
printf("buf %s\n", buf);