一、前言
在后台程序运行出问题时,C语言提供很多的预处理触发点文件名、行号、函数名的方法,关键是利用C99新增的预处理标识符__VA_ARGS__;先介绍几个编译器内置的宏定义,这些宏定义不仅可以帮助我们完成跨平台的源码编写,灵活使用也可以巧妙地帮我们输出非常有用的调试信息。
二、ANSI C标准宏
__LINE__ // 在源代码中插入当前源代码行号 __FILE__ // 在源文件中插入当前源文件名 __DATE__ // 在源文件中插入当前的编译日期 __TIME__ // 在源文件中插入当前编译时间 __STDC__ // 当要求程序严格遵循ANSI C标准时该标识被赋值为1 __VA_ARGS__ // 是一个可变参数的宏,这个可宏是新的C99规范中新增的, // 目前似乎gcc和VC6.0之后的都支持(VC6.0的编译器不支持)。 // 宏前面加上##的作用在于,可以接受参数为0个或者多个
三、实例
宏实例:
1 #include <stdio.h> 2 3 int main() 4 { 5 printf("__func__:%s\n", __func__); 6 printf("__FILE__:%s\n", __FILE__); 7 printf("__DATE__:%s\n", __DATE__); 8 printf("__TIME__:%s\n", __TIME__); 9 printf("__LINE__:%d\n", __LINE__); 10 11 return 0; 12 }
宏实例程序输出如下:
1 请按 ENTER 或其它命令继续 2 __func__:main 3 __FILE__:test1.c 4 __DATE__:Apr 17 2022 5 __TIME__:22:43:02 6 __LINE__:9
四、#和##运算符
其中#和##运算符的功能有所不同,在这里也做一定的介绍
1. #用来把参数转换成字符串
实例1:
1 #include <stdio.h> 2 3 #define P(A) printf("%s:%d\n", #A, A); 4 5 int main() 6 { 7 int a = 2, b = 4; 8 P(a); 9 P(b); 10 P(a+b); 11 12 return 0; 13 }
实例1程序输出如下:
1 请按 ENTER 或其它命令继续 2 a:2 3 b:4 4 a+b:6
实例2:
1 #include <stdio.h> 2 3 #define SQUARE(x) printf("The square of "#x" is %d.\n", ((x)*(x))) 4 5 int main() 6 { 7 SQUARE(9); 8 9 return 0; 10 }
实例2程序输出如下:
1 请按 ENTER 或其它命令继续 2 The square of 9 is 81.
2. ##运算符可以用于宏函数的替换部分
##就是个粘合剂,将前后两部分粘合起来,也就是有“组成变量名”的意思。特别要和#运算符的功能区分开来,#是连接字符串,而##是连接变量名。
但是“##”不能随意粘合任意字符,必须是合法的C语言标示符。在单一的宏定义中,最多可以出现一次“#”或“##”预处理操作符。如果没有指定与“#”或“##”预处理操作符相关的计算次序,则会产生问题。为避免该问题,在单一的宏定义中只能使用其中一种操作符(即,一份“#”或一个“##”,或都不用)。除非非常有必要,否则尽量不要使用“#”和“##”。
实例程序:
1 #include <stdio.h> 2 3 #define XNAME(n) SYSTEML_ ## n 4 5 int main() 6 { 7 int SYSTEML_ = 0, 8 SYSTEML_OPEN = 1, 9 SYSTEML_WRITE = 2, 10 SYSTEML_CLOSE = 3; 11 12 printf("%d\n", XNAME()); 13 printf("%d\n", XNAME(OPEN)); 14 printf("%d\n", XNAME(WRITE)); 15 printf("%d\n", XNAME(CLOSE)); 16 17 return 0; 18 }
实例程序输出:
1 请按 ENTER 或其它命令继续 2 0 3 1 4 2 5 3
此外,__VA_ARGS__ 是一个可变参数的宏,很少人知道这个宏,这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持。实现思想就是宏定义中参数列表的最后一个参数为省略号(也就是三个点)。
##__VA_ARGS__ 宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的逗号去掉的作用,否则会编译出错,相当于能够接受0个及以上的参数。
##__VA_ARGS__的实例:
1 1 #define my_print1(fmt,...) printf(fmt, __VA_ARGS__) 2 2 my_print1("i=%d,j=%d\n",i,j) // 可以打印 3 3 4 4 #define my_print2(fmt,...) printf(fmt, ##__VA_ARGS__) 5 5 my_print2("iiijjj\n") // 可以打印 6 6 my_print2("i=%d,j=%d\n",i,j) // 也可以打印
最终的代码为:
1 #include <stdio.h> 2 3 //用#把参数转成字符串 4 #define P(A) printf("%s: %d\n", #A, A) 5 6 #define SQUARE(x) printf("The square of "#x" is %d.\n", ((x)*(x))) 7 8 #define XNAME(n) SYSTEML_ ## n 9 10 #define my_print1(fmt, ...) printf(fmt, __VA_ARGS__) 11 #define my_print2(fmt, ...) printf(fmt, ##__VA_ARGS__) 12 13 int main(int argc, char **argv) 14 { 15 //printf("Hello world!\n"); 16 printf("__func__: %s\n", __func__); 17 printf("__FILE__: %s\n", __FILE__); 18 printf("__DATE__: %s\n", __DATE__); 19 printf("__TIME__: %s\n", __TIME__); 20 printf("__LINE__: %d\n", __LINE__); 21 22 int a = 2; 23 int b = 4; 24 P(a); 25 P(b); 26 P(a+b); 27 28 SQUARE(9); 29 30 int SYSTEML_ = 0; 31 int SYSTEML_OPEN = 1; 32 int SYSTEML_WRITE = 2; 33 int SYSTEML_CLOSE = 3; 34 35 printf("%d\n", XNAME()); 36 printf("%d\n", XNAME(OPEN)); 37 printf("%d\n", XNAME(WRITE)); 38 printf("%d\n", XNAME(CLOSE)); 39 40 int i = 3; 41 int j = 6; 42 43 my_print1("i = %d, j = %d\n", i, j); 44 my_print2("%d%d%d%d%d%d\n", i,i,i,j,j,j); 45 my_print2("444888\n"); 46 47 return 0; 48 }
程序都很简单,不多说了,自己看吧
1 #include <stdio.h>2
3 #define XNAME(n) SYSTEML_ ## n
4
5 int main()
6 {
7 int SYSTEML_ = 0,
8 SYSTEML_OPEN = 1,
9 SYSTEML_WRITE = 2,
10 SYSTEML_CLOSE = 3;
11
12 printf("%d\n", XNAME());
13 printf("%d\n", XNAME(OPEN));
14 printf("%d\n", XNAME(WRITE));
15 printf("%d\n", XNAME(CLOSE));
16
17 return 0;
18 }