你了解的C语言预处理可能只是冰山一角,来更加全面的了解一下C语言预处理吧

在这里插入图片描述

翻译程序的第一步

  1. 在预处理之前,编译器必须对该程序进行一些翻译处理
  2. 编译器把源代码中出现的字符映射到源字符集
  3. 编译器定位每个反斜杠后面跟着换行符
  4. 编译器把文本划分成预处理记号序列、空白序列和注释序列
  5. 程序已经准备好进入预处理阶段,预处理器查找一行中#号开始的预处理指令

明示常量:#define

  • #define预处理指令和其他预处理指令一样,以#号作为一行的开始。ANSI和后来的标准都允许#号前面有空格或制表符,而且还允许在#号和指令的其余部分之间有空格
  • 预处理指令从#开始运行,到后面的第1个换行符为止,也就是说,指令的长度仅限于一行每行#define(逻辑行)都由3部分组成。第1部分是#define指令本身,第2部分是选定的缩写,也成为宏,第3部分称为替换列表或替换体。
  • 一旦预处理器在程序中找到宏的示实例后,就会用替换体代替该宏。
  • 从技术角度看,可以把宏的替换体看作是记号型字符串,而不是字符型字符串
#include<stdio.h>
#define TWO 2
#define OW "Consistency is the last refuge of the unimagina tive. -Oscar Wilde"
#define FOUR TWO*TWO
#define PX printf("X is %d.\n",x)
#define FMT "X is %d.\n"
int main()
{
    
    
    int X = TWO;
    printf(FMT, X);
    printf("%s\n",OW);
    printf("TWO:OW\n");
    return 0;
}

程序运行结果:

X is 2.
Consistency is the last refuge of the unimagina tive. -Oscar Wilde
TWO:OW

重定义常量

假设把LIMIT定义为20,稍后在该文件中又把它定义为25。这个过程为重定义常量

在#define中使用参数

在#define中使用参数可以创建外形和作用与函数参数类似的类函数宏。带有参数的宏看上去很像函数,因为这样的宏也使用圆括号。类函数宏定义的圆括号中可以有一个或多个参数,随后这些参数出现在替换体中

#include<stdio.h>
#define SQUARE(X) X*X
#define PR(X) printf("The result is %d.\n",X)
int main()
{
    
    
	int x = 5;
	int z;
	printf("x=%d\n", x);
	z = SQUARE(x);
	printf("Evaluating SQUARE(x):");//5*5
	PR(z);
	printf("Evaluating SQUARE(x+2):");//5+2*5+2
	PR(SQUARE(x + 2));
}

程序运行结果:

x=5
Evaluating SQUARE(x):The result is 25.
Evaluating SQUARE(x+2):The result is 17.

用宏参数创建字符串:#运算符

C允许在字符串中包含宏参数,那么#x就是转换为字符串“‘x’的形参名,这个过程称为字符串化

#include<stdio.h>
#define PSQR(x) printf("The square of "#x" is %d.\n",((x)*(x)));
int main()
{
    
    
	int y = 5;
	PSQR(y);
	PSQR(2 + 4);
	return 0;

}

程序运行结果:

The square of y is 25.
The square of 2 + 4 is 36.

预处理器粘合剂:##运算符

##运算符可用于类函数宏的替换部分,##还可用于对象宏的替换部分,##运算符把两个记号组合成一个记号

#include<stdio.h>
#define XNAME(n) x ## n
#define PRINT_XN(n) printf("x" #n "=%d\n",x##n);
int main()
{
    
    
	int XNAME(1) = 14;//变成int x1=14;
	int XNAME(2) = 20;//变成int x2=20;
	int x3 = 30;
	PRINT_XN(1);//变成printf("x1=%d\n",x1);
	PRINT_XN(2);//变成printf("x2=%d\n",x2);
	PRINT_XN(3);//变成printf("x3=%d\n",x3);
	return 0;
}

程序运行结果:

x1=14
x2=20
x3=30

注意:PRINT_XN()宏用#运算符组合字符串,##运算符把记号组合成一个新的标识符

宏和函数的选择

  • 有些编程任务既可以用带参数的宏完成,也可以用函数完成。应该使用宏还是函数?
  • 使用宏稍有不慎就会产生奇怪的副作用
  • 宏和函数的选择实际上是时间和空间的权衡。宏生成内联代码,即在程序中生成语句。如果调佣20次宏,即在程序中插入20行代码。如果调用函数20次,程序中只有一份函数语句的副本,所以节省了空间,然后另一个方面,程序的控制必须跳转至函数内,随后再返回主调函数,这显然比内联代码花费更多的时间。
注意事项
  1. 宏名中不能有空格,但是替换字符串中可以有空格,ANSI C允许在参数列表中使用空格
  2. 用圆括号把宏的参数和整个替换体括起来。这样能确保被括起来的部分在下面这样的表达式中正确地展开
  3. 用大写字母表示宏参数的名称、大写字母可以提醒程序员注意,宏可能产生的副作用
  4. 如果打算使用宏来加快程序的运行速度,那么首先要确定使用宏和使用函数是否会导致较大的差异。

文件包含:#include

为什么要包含文件?

因为编译器需要这些文件中的信息

#include指令有两种形式:

文件名在尖括号中:尖括号告诉预处理器在标准系统目录中查找该文件

#include<stdio.h>

文件名在双引号中:双引号告诉预处理编译器首先在当前目录中(或文件名中指定的其他目录)查找该文件,如果未找到再查找标准系统目录

#include"test.h"

#undef指令

#undef指令用于“取消”已定义的#define指令

#define LIMIT 400
#undef LIMIT //将上面定义移除,如果想想使用一个名称,有不确定之前是否使用过,为安全起见,可以用#undef指令取消该名字的定义

条件编译

使用这些指令告诉编译器根据编译时的条件执行或忽略信息(或代码)块

#ifdef、#else和endif指令

#ifdef指令说明,如果预处理已定义了后面的标识符,则执行#else或#endif指令之前的所有指令并编译所有C代码(先出现哪个智指令就执行到哪里。

#ifndef指令

#ifndef指令和#ifdef指令的用法类似,也可以和#else,#endif一起使用,但是它们的逻辑相反。#ifndef指令判断后面的标识符是否是未定义的,常用于定义之前未定义的常量

#if和#elif指令

#if指令很像C语言中的if。#if后面跟整型常量表达式。如果表达式为非零,则表达式为真,可以在指令中使用C的关系运算符和逻辑运算符

#if SYS==1
	#include<stdio.h>
#elif SYS==2
	#include<string.h>
elif SYS==3
	#include<time.h>
else
    #include<ctype.h>
#endif

猜你喜欢

转载自blog.csdn.net/weixin_51799303/article/details/130655483