目录
今天接着继续讲解预处理的点,前面已经深入学习了#define。
#undef 这条指令用于移除一个宏定义。
#undef NAME
如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。
- #include
- #define M 10
- int main()
- {
- int a = M;
- printf("%d\n", a);
- #undef M
- #define M 100
- int b = M;
- printf("%d", b);
- return 0;
- }
命令行定义是什么呢?
许多C 的编译器提供一种能力,允许在命令行中定义符号。用于启动编译预处理阶段过程。
命令行是在命令行中给一些符号指定值。
例如:当我们根据同一个源文件要编译出不同的一个程序的不同版本的时候,这个特性有点用处。(假定某个程序中声明了一个某个长度的数组,如果机器内存有限,我们需要一个很小的数组,但是另外一个机器内存大写,我们需要一个数组能够大写。)
下面到gcc编译器上验证一下。
- #include
- int main()
- {
- int arr[SZ];
- int i = 0;
- for (i = 0; i < SZ; i++)
- {
- arr[i] = i;
- }
- for (i = 0; i < SZ; i++)
- {
- printf("%d ", arr[i]);
- }
- return 0;
- }
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。
条件编译是指满足条件就编译,不满足条件就不编译。
哪些地方会用到条件编译呢?
比如 :调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译。还有当我们写的代码有跨平台的使用,一个文件有windows底下使用的代码,又含有Linx底下使用的代码。准对不同的平台,编译环境的不同,我们需要选择性的编译代码。
接下来,我们介绍一下常见的条件编译。
常量表达式为真 的时候参与编译,为假 的时候不参与编译,#if 和 #endif 是配套的。
- 1.
- #if 常量表达式
- //...
- #endif
- //常量表达式由预处理器求值。
- #include
- int main()
- {
- #if 1
- printf("hehe\n");
- #endif
- return 0;
- }
- 2.多个分支的条件编译
- #if 常量表达式
- //...
- #elif 常量表达式
- //...
- #else
- //...
- #endif
- #include
- int main()
- {
- #if 0
- printf("hehe\n");
- #elif 2==1
- printf("haha\n");
- #elif 3==1
- prntf("heihei\n");
- #elif 1
- printf("xixi\n");
- #endif
- return 0;
- }
如果多个表达式为真呢?
- #include
- int main()
- {
- #if 0
- printf("hehe\n");
- #elif 1==1
- printf("haha\n");
- #elif 3==1
- prntf("heihei\n");
- #elif 1
- printf("xixi\n");
- #endif
- return 0;
- }
- 3.判断是否被定义
-
- #if defined(symbol)//第一种写法
- #ifdef symbol//第二种写法
- //反之
- #if !defined(symbol)
- #ifndef symbol
- //两种写法一样的效果
- #include
- #define M 0
- int main()
- {
- #if defined(M)//不是看M的真假而是看M是否被定义过没有
- printf("hehe\n");
- #endif
-
- #ifdef M//两种写法都可以
- printf("hehe\n");
- #endif
- return 0;
- return 0;
- }
同理 !defined
- #if defined(OS_UNIX)
- #ifdef OPTION1
- unix_version_option1();
- #endif
- #ifdef OPTION2
- unix_version_option2();
- #endif
- #elif defined(OS_MSDOS)
- #ifdef OPTION2
- msdos_version_option2();
- #endif
- #endif
在我们的头文件#include
我们已经知道, #include 指令可以使另外一个文件被编译。就像它实际出现于 #include 指令的地方一样。
这种替换的方式很简单: 预处理器先删除这条指令,并用包含文件的内容替换。 这样一个源文件被包含10次,那就实际被编译10次。
#include"test.h"
查找策略:
Linux环境的标准头文件的路径:
VS环境的标准头文件的路径:(不确定--建议自己按照自己安装的路径去查找)
#include
综上所诉: 这样是不是可以说,对于库文件也可以使用" "的形式包含? 答案是肯定的,可以。
但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。
头文件的包含有两种形式:
- 包含本地文件(自己.h文件) #include"xxx.h"
- 包含标准库的头文件 #include
#include"xxx.h"
在大型工程项目中出现下面这种情况,该怎么办?
comm.h和comm.c是公共模块。 test1.h和test1.c使用了公共模块。 test2.h和test2.c使用了公共模块。 test.h和test.c使用了test1模块和test2模块。 这样最终程序中就会出现两份comm.h的内容。这样就造成了文件内容的重复。
解决办法:条件编译。
每个头文件的开头写:
- #ifndef __TEST_H__
- //定义了的话将不在定义,未定义的话编译执行下面代码
- #define __TEST_H__//头文件的内容
- //定义
- ...
- ....
- #endif //__TEST_H__
或者写这个:
#pragma once
就可以避免头文件重复引入的问题了。
注:推荐《高质量C/C++编程指南》里面有这样两道笔试题:
1. 头文件中的 ifndef/define/endif是干什么用的?
2. #include和 #include "filename.h"有什么区别?
学完上面的知识,相信你心中一定有了答案了。
- #error
- #pragma
- #line
- ...
- #pragma pack()在结构体部分介绍。
后前会更新一个专栏去学习《C语言深度解剖》 这本书。大家要乖乖敲代码哦。
✔✔✔✔✔最后感谢大家的阅读,若有错误和不足,欢迎指正!
代码---------→【gitee:唐棣棣 (TSQXG) - Gitee.com】
联系---------→【邮箱:2784139418@qq.com】