在C程序中,以#开头的所有行均由预处理器处理,预处理器是由编译器调用的特殊程序。这样,我们的意思是说“#”符号用于处理程序中的其他语句之前的功能,也就是说,这意味着它在运行时之前或在编译时处理某些代码。用最基本的术语来说,预处理器采用一个C程序并生成另一个没有任何#的C程序。
以下是有关C中预处理器的一些有趣事实。
1)当我们使用include指令时,包含的头文件(经过预处理)的内容被复制到当前文件中。
尖括号<和>指示预处理器在保存所有头文件的标准文件夹中查找。用双引号“和”指示预处理器查看当前文件夹(当前目录)。
2)当我们使用define作为常量时,预处理器会生成一个C程序,在该程序中搜索定义的常量,并用给定的表达式替换匹配的标记。例如,在下面的程序中, max定义为100。
C
#include
#define max 100
int main()
{
printf("max is %d", max);
return 0;
}
C
#include
#define INCREMENT(x) ++x
int main()
{
char *ptr = "GeeksQuiz";
int x = 10;
printf("%s ", INCREMENT(ptr));
printf("%d", INCREMENT(x));
return 0;
}
C
#include
#define MULTIPLY(a, b) a*b
int main()
{
// The macro is expanded as 2 + 3 * 3 + 5, not as 5*8
printf("%d", MULTIPLY(2+3, 3+5));
return 0;
}
// Output: 16
C
#include
//here, instead of writing a*a we write (a)*(b)
#define MULTIPLY(a, b) (a)*(b)
int main()
{
// The macro is expanded as (2 + 3) * (3 + 5), as 5*8
printf("%d", MULTIPLY(2+3, 3+5));
return 0;
}
// This code is contributed by Santanu
C
#include
#define merge(a, b) a##b
int main()
{
printf("%d ", merge(12, 34));
}
C
#include
#define get(a) #a
int main()
{
// GeeksQuiz is changed to "GeeksQuiz"
printf("%s", get(GeeksQuiz));
}
C
#include
#define PRINT(i, limit) while (i < limit) \
{ \
printf("GeeksQuiz "); \
i++; \
}
int main()
{
int i = 0;
PRINT(i, 3);
return 0;
}
C
#include
#define square(x) x*x
int main()
{
// Expanded as 36/6*6
int x = 36/square(6);
printf("%d", x);
return 0;
}
C
#include
static inline int square(int x) { return x*x; }
int main()
{
int x = 36/square(6);
printf("%d", x);
return 0;
}
C
int main()
{
#if VERBOSE >= 2
printf("Trace Message");
#endif
}
C
#include
int main()
{
printf("Current File :%s\n", __FILE__ );
printf("Current Date :%s\n", __DATE__ );
printf("Current Time :%s\n", __TIME__ );
printf("Line Number :%d\n", __LINE__ );
return 0;
}
C
#include
#define LIMIT 100
int main()
{
printf("%d",LIMIT);
//removing defined macro LIMIT
#undef LIMIT
//Next line causes error as LIMIT is not defined
printf("%d",LIMIT);
return 0;
}
//This code is contributed by Santanu
C
#include
#define LIMIT 1000
int main()
{
printf("%d",LIMIT);
//removing defined macro LIMIT
#undef LIMIT
//Declare LIMIT as integer again
int LIMIT=1001;
printf("\n%d",LIMIT);
return 0;
}
C
#include
//div function prototype
float div(float, float);
#define div(x, y) x/y
int main()
{
//use of macro div
//Note: %0.2f for taking two decimal value after point
printf("%0.2f",div(10.0,5.0));
//removing defined macro div
#undef div
//function div is called as macro definition is removed
printf("\n%0.2f",div(10.0,5.0));
return 0;
}
//div function definition
float div(float x, float y){
return y/x;
}
//This code is contributed by Santanu
max is 100
3)宏可以采用类似参数的函数,不检查参数的数据类型。例如,下面的宏INCREMENT(x)可以用于任何数据类型的x。
C
#include
#define INCREMENT(x) ++x
int main()
{
char *ptr = "GeeksQuiz";
int x = 10;
printf("%s ", INCREMENT(ptr));
printf("%d", INCREMENT(x));
return 0;
}
eeksQuiz 11
4)宏扩展之前不评估宏参数。例如,考虑以下程序
C
#include
#define MULTIPLY(a, b) a*b
int main()
{
// The macro is expanded as 2 + 3 * 3 + 5, not as 5*8
printf("%d", MULTIPLY(2+3, 3+5));
return 0;
}
// Output: 16
16
可以使用以下程序解决先前的问题
C
#include
//here, instead of writing a*a we write (a)*(b)
#define MULTIPLY(a, b) (a)*(b)
int main()
{
// The macro is expanded as (2 + 3) * (3 + 5), as 5*8
printf("%d", MULTIPLY(2+3, 3+5));
return 0;
}
// This code is contributed by Santanu
40
5)传递给宏的令牌可以使用称为令牌粘贴运算符运算符##进行级联。
C
#include
#define merge(a, b) a##b
int main()
{
printf("%d ", merge(12, 34));
}
1234
6)传递给宏的令牌可以通过在其前面使用#转换为字符串字面量。
C
#include
#define get(a) #a
int main()
{
// GeeksQuiz is changed to "GeeksQuiz"
printf("%s", get(GeeksQuiz));
}
GeeksQuiz
7)宏可以使用’\’写入多行。最后一行不需要带有“ \”。
C
#include
#define PRINT(i, limit) while (i < limit) \
{ \
printf("GeeksQuiz "); \
i++; \
}
int main()
{
int i = 0;
PRINT(i, 3);
return 0;
}
GeeksQuiz GeeksQuiz GeeksQuiz
8)应避免使用带有参数的宏,因为它们有时会引起问题。并且应该首选内联函数,因为内联函数中有类型检查参数评估。从C99开始,C语言也支持内联函数。
例如,考虑以下程序。乍一看,输出似乎为1,但输出为36。
C
#include
#define square(x) x*x
int main()
{
// Expanded as 36/6*6
int x = 36/square(6);
printf("%d", x);
return 0;
}
36
如果使用内联函数,则将获得预期的输出。同样,可以使用内联函数来更正上面第4点中给出的程序。
C
#include
static inline int square(int x) { return x*x; }
int main()
{
int x = 36/square(6);
printf("%d", x);
return 0;
}
1
9)预处理器还支持if-else指令,这些指令通常用于条件编译。
C
int main()
{
#if VERBOSE >= 2
printf("Trace Message");
#endif
}
No Output
10)头文件可能被多次直接或间接包含,这导致重新声明相同变量/函数的问题。为了避免这个问题,指示等规定,IFDEF和IFNDEF使用。
11)有一些标准宏可用于打印程序文件(__FILE__),编译日期(__DATE__),编译时间(__TIME__)和C代码中的行号(__LINE__)
C
#include
int main()
{
printf("Current File :%s\n", __FILE__ );
printf("Current Date :%s\n", __DATE__ );
printf("Current Time :%s\n", __TIME__ );
printf("Line Number :%d\n", __LINE__ );
return 0;
}
Current File :/usr/share/IDE_PROGRAMS/C/other/081c548d50135ed88cfa0296159b05ca/081c548d50135ed88cfa0296159b05ca.c
Current Date :Sep 4 2019
Current Time :10:17:43
Line Number :8
12)我们可以使用删除已经定义的宏:
#undef MACRO_NAME
C
#include
#define LIMIT 100
int main()
{
printf("%d",LIMIT);
//removing defined macro LIMIT
#undef LIMIT
//Next line causes error as LIMIT is not defined
printf("%d",LIMIT);
return 0;
}
//This code is contributed by Santanu
删除先前定义的宏LIMIT后,我们已将LIMIT声明为整数变量,因此以下程序已正确执行
C
#include
#define LIMIT 1000
int main()
{
printf("%d",LIMIT);
//removing defined macro LIMIT
#undef LIMIT
//Declare LIMIT as integer again
int LIMIT=1001;
printf("\n%d",LIMIT);
return 0;
}
1000
1001
关于使用( #undef )宏的另一个有趣的事实
C
#include
//div function prototype
float div(float, float);
#define div(x, y) x/y
int main()
{
//use of macro div
//Note: %0.2f for taking two decimal value after point
printf("%0.2f",div(10.0,5.0));
//removing defined macro div
#undef div
//function div is called as macro definition is removed
printf("\n%0.2f",div(10.0,5.0));
return 0;
}
//div function definition
float div(float x, float y){
return y/x;
}
//This code is contributed by Santanu
2.00
0.50