预处理详解(二)
目录
- #与##运算符
- #运算符
- ##运算符
- 被称为记号粘合
- 命名约定
- #undef
- 条件编译
- 常见的条件编译指令
- 头文件的包含
- 本地头文件包含
- 库函数头文件包含
- 嵌套文件包含
#与##运算符
#运算符
当我们有⼀个变量 int a = 10; 的时候,我们想打印出: the value of a is 10 就可以写:
#define PRINT(n) printf("the value of "#n " is %d", n);
当我们按照下⾯的方式调用的时候:
PRINT(a);当我们把a替换到宏的体内时,就出现了#a,⽽#a就是转换为"a"⼀个字符。
代码就会被预处理为:
printf("the value of ""a" " is %d", a);
##运算符
##可以把位于它两边的符号合成⼀个符号,它允许宏定义从分离的文本片段建标识符。
被称为记号粘合
这样的连接必须产⽣⼀个合法的标识符。否则其结果就是未定义的。
写⼀个函数求2个数的较⼤值的时候,不同的数据类型就得写不同的函数。但是宏不用。
比如:
//宏定义
#define GENERIC_MAX(type) \
type type##_max(type x, type y)\
{ \return (x>y?x:y); \
}
GENERIC_MAX(int) //替换到宏体内后int##_max ⽣成了新的符号 int_max做函数名
GENERIC_MAX(float) //替换到宏体内后float##_max ⽣成了新的符号 float_max做函数名
int main()
{int m = int_max(2, 3);printf("%d\n", m);float fm = float_max(3.5f, 4.5f);printf("%f\n", fm);return 0;
}
但是这个在实际开发中用的很少,了解一下即可。
命名约定
⼀般来讲函数的宏的使宏语法很相似。所以语言本⾝没法帮我们区分⼆者。
那我们平时的⼀个习惯是:
把宏名全部大写
函数名不要全部大写
#undef
它的作用是移除一个宏定义:
#define M 100
int main()
{printf("the value = %d\n",M);
#undef Mprintf("the value = M");return 0;
}
条件编译
在编译⼀个程序的时候我们如果要将⼀条语句(⼀组语句)编译或者放弃是很⽅便的。因为我们有条件编译指令。
比如:
一些用来调试的代码检查这段代码是否执行,调试完后删了可惜,保留又没什么用,就可以用条件编译指令。
#include <stdio.h>
#define __DEBUG__
int main()
{int i = 0;int arr[10] = {0};for(i=0; i<10; i++){arr[i] = i;#ifdef __DEBUG__printf("%d\n", arr[i]);//为了观察数组是否赋值成功。 #endif //__DEBUG__}return 0;
}
当我们定义了__DEBUG__,就会打印,没有定义就直接跳到endif。
常见的条件编译指令
1.
#if 常量表达式//...
#endif
//常量表达式由预处理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__//..
#endif
2.多个分⽀的条件编译
#if 常量表达式//...
#elif 常量表达式//...
#else//...
#endif
3.判断是否被定义
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
4.嵌套指令
#if defined(OS_UNIX)#ifdef OPTION1unix_version_option1();#endif#ifdef OPTION2unix_version_option2();#endif
#elif defined(OS_MSDOS)#ifdef OPTION2msdos_version_option2();#endif
#endif
头文件的包含
本地头文件包含
#include "test.h"
这是我们包含自己创建的头文件的方式,当我们使用这种方式包含头文件,编译器会在我们创建工程的地方寻找这个头文件,当工程文件找不到,才会去标准库函数的文件里面去找。
库函数头文件包含
#include <filename.h>
查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。
这样是不是可以说,对于库⽂件也可以使⽤ “” 的形式包含?
答案是肯定的,可以,但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。
要是用库函数的形式包含自建头文件,就会先去标准库里面查找,找不到才会在工程文件里面找,会浪费时间。
嵌套文件包含
在我们写程序时可能一个头文件要在多个.c文件里面包含,预编译时会将头文件替换成它的函数内容,这样在编译时就会出现重复编译,使得代码很繁琐。
这个时候我们就可以用条件编译来解决这个问题:
#ifndef __test_H_//是否定义了“__test_H_”
//没有定义就编译,定义过就跳转到#endif
#define __test_H_
void 函数名();
#endif
这样在编译时,第一次编译头文件,因为前面的代码是否定义了“_test_H”,没有就编译一次,当第二次时,因为前面已经定义过了,所以就不会进行二次编译,这样就避免了重复编译的可能。
预编译指令还有很多,有兴趣的可以参考《C语言深度解剖》学习
---------------------------------------------------分隔符
对预处理的介绍就结束了,感谢各位观看。
有错请在评论区指正,共勉!