想了想不能偷懒,今日事今日毕,必须更完。
(下篇)
#define定义宏和函数的对比
预处理操作符#和##的介绍
预处理指令 #undef
命令行定义(不重要)
3.条件编译
4.文件包含:预处理指令 #include
一.预处理
1.#和##
如果我们想要分别对于a、b、c变量打印这样的一句话the value of a is 10、the value of b is 20、the value of c is 30,封装函数是很难实现的,但宏可以做到。
首先要明白上图打印出来的效果是一样的,这是基础。接着可以写一个这样的宏:在N前面加#号,在#N前后加双引号,使得#N被识别
#X的作用就是,编译器在预处理阶段,若宏定义中的参数前面有#,我们不进行替换,我们把调用宏的宏参数名字变成对应的字符串。
##的作用:##可以把位于它两边的符号合成一个符号。它允许宏定义从分离的文本片段创建标识符。下面的例子就是把name和num合成一个符号,调用,就相当于printf("%d\n",class105);,打印结果为105
2.带副作用的宏参数
这个程序给b赋值,但第3行改变了a的值,第2行没有,这时候就说第3行具有副作用。
那么对于宏参数也不例外,下面的程序其实就修改了a和b值。
3.宏和函数的对比
宏优点:
1.我们使用函数,函数的调用工作和返回工作就那么多,用于调用和从函数返回的代码很可能比实际执行这个小型计算工作的代码更大,所以使用宏比使用函数在程序的规模和速度方面都更胜一筹。
2.更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之上面这个宏可以适用于整形、长整型、浮点型等,这些都可以用于>来比较的类型。宏是类型无关的。
宏缺点:
-
每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序
的长度。 -
宏是没法调试的。
-
宏由于类型无关,也就不够严谨。
-
宏可能会带来运算符优先级的问题,导致程容易出现错。
这里注意一点,宏要全部大写
还有一个小知识点:#undef,用于移除一个宏定义,也就是如果一个现存的名字需要被重新定义,那么它的旧定义首先用#undef移除。
4.命令行定义
这个不是很重要,了解一下就行。
一些C编译器有一种能力,允许在命令行中定义符号,用于启动编译过程。
当我们根据同一个源文件编译一个程序的不同版本时,这个特性是很有用的,假如一个程序声明了某种长度的数组,某个机器的内存很有限,这个数组必须很小,但在另一个内存比较充裕的机器上,希望这个数组能够大一些,我们就可以以下面方式声明:
这个程序在Linux中,可以利用指定M的值(当然VS不行)
二.条件编译
满足条件编译,不满足条件,则不编译,使用条件编译你可以选择代码的一部分是被正常编译还是完全忽略。
1. 最普通的
2.多分支
3.判断有无定义
4.嵌套
三.文件包含
1.本地文件包含和库文件包含:
2.嵌套文件包含
comm.h和comm.c是公共模块。
test1.h和test1.c使用了公共模块。
test2.h和test2.c使用了公共模块。
test.h和test.c使用了test1模块和test2模块。
这样最终程序中就会出现两份comm.h的内容。这样就造成了文件内容的重复。】
那么如何解决呢?其实就用刚才写的条件编译:
在每个头文件的开头写:
或者#pragma once,我们在VS中创建头文件的时候第一行默认就是这个指令。
差不多写完了。