目录
6、C语言中的宏和预处理
在 C 语言编程中,所有以 # 开头的的内容都会被预处理器进行预处理,预处理器是一个特殊的程序,由编译器调用。编译器的目的就是将一个 C 语言程序转化为另一个不含任何 # 的 C 语言程序。
下面是一些有关 C 语言预处理器的有趣的事实:
-
当我们使用 include 指令时,在预处理后,头文件中的内容将被复制到当前文件中,尖括号 < 和 > 用于从标准头文件库中导入,而双引号 “” 用于从当前文件夹下导入头文件(一般尖括号用于导入系统级标准头文件,双引号多用于引入自定义头文件)
-
当我们用 define 定义一个常量,预处理器将查找匹配给定的常量名称,并用给定的表达式进行替换,例如下面的程序中,max 被定义为 1000
#include<stdio.h> #define max 100 int main() { printf("max is %d", max); } // output: max is 100
-
宏类似类似一个函数,这个函数的参数不需要检查数据类型,例如下面的宏定义
INCREMENT(x)
可以用于任意的数据类型。#include <stdio.h> #define INCREMENT(x) ++x int main() { char *ptr = "GeeksQuiz"; int x = 10; printf("%s ", INCREMENT(ptr)); printf("%d", INCREMENT(x)); return 0; } //output: eeksQuiz 11
-
宏参数在宏扩展之前没有被执行。例如,下面这段程序:
#include <stdio.h> #define MULTIPLY(a, b) a*b int main() { // The macro is expended as 2 + 3 * 3 + 5, not as 5*8 printf("%d", MULTIPLY(2+3, 3+5)); return 0; } // Output: 16
上面的问题,可以通过下面的方式进行解决:
#include <stdio.h> //here, instead of writing a*a we write (a)*(b) #define MULTIPLY(a, b) (a)*(b) int main() { // The macro is expended as (2 + 3) * (3 + 5), as 5*8 printf("%d", MULTIPLY(2+3, 3+5)); return 0; } //This code is contributed by Santanu //output: 40
-
通过宏定义的字符可以通过操作符 ## 串联起来,这个操作符称作 Token-Pasting 操作符。
#include <stdio.h> #define merge(a, b) a##b int main() { printf("%d ", merge(12, 34)); } //output: 1234
-
通过宏定义的参数可以在定义时候,前面加个 # ,可以将传入的参数转为字符串
#include <stdio.h> #define get(a) #a int main() { // GeeksQuiz is changed to "GeeksQuiz" printf("%s", get(GeeksQuiz)); } //output: GeeksQuiz
-
宏定义可以通过 \ 写到多行中,最后一行不需要
#include <stdio.h> #define PRINT(i, limit) while (i < limit) \ { \ printf("GeeksQuiz "); \ i++; \ } int main() { int i = 0; PRINT(i, 3); return 0; } //output: GeeksQuiz GeeksQuiz GeeksQuiz
-
应避免使用带有参数的宏,因为它们有时会导致问题。这时候内联函数应该是首选的,因为在内联函数中有类型检查。从 C99 开始,C语言也支持内联函数。
#include <stdio.h> #define square(x) x*x int main() { // Expanded as 36/6*6 int x = 36/square(6); printf("%d", x); return 0; } //output: 36
如果我们使用内联函数,就会得到预期的输出。另外,上面第 4 点中给出的程序可以使用内联函数进行修正。
#include <stdio.h> static inline int square(int x) { return x*x; } int main() { int x = 36/square(6); printf("%d", x); return 0; } //output: 1
-
预处理器还支持 if-else 语句,可直接用于条件选择
int main() { #if VERBOSE >= 2 printf("Trace Message"); #endif } # No Output
-
一个头文件可能直接或间接的不止一次被引入,这会导致相同的变量/函数存在重定义的问题,为了避免这个问题,应该使用 defined, ifdef 和 ifndef。
-
有一些标准的宏定义可以用于输出程序文件目录(FILE)、编译日期(DATE)、编译时间(TIME)以及代码行数(LINE)
#include <stdio.h> 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 :9 */
-
移除已经定义的宏
undef MACRO_NAME
#include <stdio.h> #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
#include <stdio.h> #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; } //output: 1000 1001
-
关于 undef 的另一个有趣的事实
#include <stdio.h>
//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
//output: 2.00 0.05