C语言预处理

预处理

预定义符号

12345
__FILE__      //进行编译的源文件__LINE__      //文件当前的行号__DATE__      //文件被编译的日期__TIME__      //文件被编译的时间__STDC__      //如果编译器遵循ANSI C,其值为1,否则未定义

这些预定义符号都是语言内置的,例如:

12345
#include <stdio.h>int main() {    printf("file: %s\nline: %d\n",__FILE__,__LINE__);    return 0;}

打印的结果是:

12
file: /Users/miles/Library/Mobile Documents/com~apple~CloudDocs/C/预处理2024_10_29_12_04/预处理2024_10_29_12_04/main.cline: 10

#define

#define定义标识符

语法:
#define name stuff

使用#define大体上就是完成替换的操作。例如#define reg register之后在程序中写rge int a;register int a;的效果是一致的。

123456789
#include <stdio.h>#define MAX 100#define STR "Hello"int main() {    int max = MAX;    printf("%d\n",max);    printf("%s\n",STR);    return 0;}

打印的结果是

12
100Hello

使用#define还可以替换函数,例如:

123456
#include <stdio.h>#define do_forever for(;;)int main() {    do_forever;    return 0;}

上面的代码就相当于在主函数中运行for(;;);,即程序死循环。

不要在#define后面加分号;,因为替换后会变成两个分号。

#define定义宏

#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)

语法:
#define name(parament-1ist) stuff
parament-list是一个由逗号隔开的符号表,可能出现在stuff中
注意⚠️:parament-list的左括号必须紧邻name。

123456789
#include <stdio.h>#define SQUARE(X) X*Xint main() {    int ret = SQUARE(5);    //上面的代码等价于    //int ret = 5*5;    printf("%d ",ret);    return 0;}

打印的结果是25。

上面的代码存在一些问题,例如:

1234567
#include <stdio.h>#define SQUARE(X) X*Xint main() {    int ret = SQUARE(5+1);    printf("%d ",ret);    return 0;}

打印的结果不是期望的36,而是11,这是运算优先级的问题导致的,即计算顺序是:5+1*5+1=11
要解决这个问题,可以把宏替换进去的内容加上括号,即(5+1)*(5+1)=36
下面是更正后的代码:

1234567
#include <stdio.h>#define SQUARE(X) (X)*(X)int main() {    int ret = SQUARE(5+1);    printf("%d ",ret);    return 0;}

上面的代码也存在一些问题,例如:

12345678
#include <stdio.h>#define Double(X) (X)+(X)int main() {    int a = 5;    int ret = 10 * Double(a);    printf("%d ",ret);    return 0;}

预期的结果是100,然而打印的结果是55,这也是由于运算优先级的问题,程序的计算方法是:10 * (5) + (5) = 55
为了解决这个问题,我们在X的最外层加上括号,即10 * ( (5) + (5) ) = 100
更正后的代码:

12345678
#include <stdio.h>#define Double(X) ((X)+(X))int main() {    int a = 5;    int ret = 10 * Double(a);    printf("%d ",ret);    return 0;}

也就是说,有关数值表达式求值的宏定义都应该加上括号,避免出现求值错误

#define 替换规则

  1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
  2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值替换。
  3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
    注意:
  4. 宏参数和#define 定义中可以出现其他#define定义的变量。但是对于宏,不能出现递归。
  5. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

#和##

12345678910
#include <stdio.h>#define PRINT(X) printf("the value of "#X" is %d\n",X)int main(){    int a = 10;    int b = 20;    PRINT(a);    PRINT(b);    return 0;}

打印的结果是:

12
the value of a is 10the value of b is 20

##可以把位于它两边的符号合成一个符号。

1234567
#include <stdio.h>#define CAT(X,Y) X##Yint main(){    int Hello = 1234;    printf("%d\n",CAT(Hel, lo));}

打印的结果是1234
注意⚠️:这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。

带有副作用的宏参数

当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。
例如:

12
x+1;//没有副作用x++;//带有副作用

使用带副作用的宏参数时一定要小心,建议不要写带副作用的宏参数

123456789101112
#include <stdio.h>#define MAX(X,Y) ((X)>(Y)?(X):(Y))int main(){    int a = 10;    int b = 11;    int max = MAX(a++,b++);    //int max = ((a++) > (b++) ? (a++) : (b++));    printf("%d ",max);    printf("%d ",a);    printf("%d\n",b);}

打印的结果是12 11 13

宏和函数对比

宏经常用于简单的运算,例如比较两个数的大小。
define MAX(X,Y) ( (X) > (Y) ? (X) : (Y) )
在执行小型运算过程中,宏比函数在程序的规模和速度方面更好,此外,宏是类型无关的。
命名习惯:宏名大写,函数名不要全部大写

#undef:移除宏定义

123456789
#include <stdio.h>#define MAX 100int main(){    printf("%d\n",MAX);#undef MAX    printf("%d\n",MAX);    //程序无法正常运行,这里MAX是未定义的}

如果现存的一个名字需要被重新定义,使用#undef来移除宏定义

条件编译

使用条件编译指令使一条或者一段语句在程序编译时放弃编译。

12345678
#include <stdio.h>int main(){    printf("hello");#if 1    printf(" world\n");#endif}

#if后面的表达式如果为真,后面的表达式参与编译,为假,不参与编译。

多分支的条件编译

语法:

1234567
#if 常量表达式	//...#elif 常量表达式	//...#else	//...#endif

举个例子:

1234567891011
#include <stdio.h>int main(){#if 1    printf("1");#elif 1    printf("2");#else    printf("3");#endif}

上例中,如果if后面的语句为真,打印1,
if后面为假且elif后面为真,打印2,
if和elif后面都为假,打印3。

判断是否被定义

123456789
//定义了执行#if defined(symbol)    //...#endif //没有定义执行#if !defined(symbol)    //...#endif

通过判断symbol是否被定义来决定是否执行语句。

12345678
#include <stdio.h>#define debugint main(){#if defined(debug)    printf("debug");#endif}

上例,如果定义了debug,则打印debug。

嵌套指令

上述的条件编译指令可以嵌套使用。

🔍 ×