第10章 编译预处理与位运算
编译预处理,是指源程序被正式编译之前所进行的处理工作。由预处理程序实现,负责分析和处理行首以‘#’开头的控制行,包括宏代换、文件包含和条件编译。
10.1 宏定义
宏定义就是把一长串字符序列用一个简短的名字去代替,这个名字称为宏名,被代替的长串序列就被称为宏值。
1. 简单宏
1) 定义格式:#define <宏名> <宏值>
2) 功能:将宏名代替宏值
3) 需要注意的问题:
(1) 宏名一般使用大写
(2) 如果宏名过长,可以使用需行符“\”
(3) 宏值为表达式时,最好用圆括号括起来
(4) 宏定义可以嵌套
(5) 若宏名出现在字符串中,则字符串中的宏名不作替换。
(6) 宏取消命令:#undef <宏名>
2. 带参数的宏
1) 定义格式:#define <宏名> <宏值(含参数的字符串)>
2) 功能:类似于函数
3) 需要注意的问题:
(1) 宏值中间的参数要用括号括起来。
(2) 当一个宏名要在另一个宏定义的宏值中出现时,最好将它的宏值部分括起来,以避免发生意外。
(3) 带参宏与函数的区别
[1] 函数调用时先对参数表达式进行计算,把计算的结果作为一个值去代替形参,而宏代换时不对参数求值。
[2] 函数的形参和实参的有类型,而宏的参数没有类型。
[3] 函数调用是在运行时处理的,要占有运行时间,而宏是在编译预处理阶段处理的,不占有运行时间。因此一些小的、调用次数频繁的函数,最好用带参的宏去代替。
10.2 文件包含
1. 一般格式:#include <文件名>|”文件名”
2. 功能:用指定文件的一份拷贝和取代这条预处理。
3. 需要注意的问题
1) <>括起来的文件,预处理程序会在标准库头文件中去查找;””括起来的文件,预处理程序会在当前正在编译的程序所在目录中查找。
2) 文件包含可以嵌套,被包含文件里包含的文件也被包含文件所包含。
3) 被包含文件的全局变量也是包含文件中的全局变量。
10.3 条件编译
一个程序中的所有语句并不一定要全部编译执行,根据一定的条件可以对其中的一部分进行编译。能够控制编译范围的指令就是C语言提供的条件编译指令。
1. #if…#endif形式
1) 一般格式:
#if <常整数表达式>
<程序段>
#elif
<程序段>
#endif
2) 功能:若常整数表达式的值为真,则编译程序段。
3) 需要注意的问题
(1) 常整数表达式中只能出现预定于的常量标识符、整常数、字符常数等。
2. #ifdef…#endif形式
1) 一般格式:
#ifdef <标识符>
<程序段>
#endif
2) 功能:如果标识符用#define定义过,则编译程序段。
3. #ifndef…#endif形式
1) 一般格式:
#ifndef <标识符>
<程序段>
#endif
2) 功能:如果标识符未被#define定义过,则编译程序段。
4. 条件编译的主要用途:
1) 忽略程序的某一部分:
#if 0
<不编译的程序段>
#endif
2) 帮助程序调试
10.4 位运算
10.4.1 位运算
1. 字符逻辑运算符
1) 按位与运算符&
2) 按位或运算符|、
3) 按位取反运算符~
4) 按位异或运算符^(相同则为0,相反则为1)
5) 需要注意的问题:
(1) 任何一个数和它自身的异或永远是0,而任何一个数和0的异或的结果是其自身。
(2) 对一个数据用另一个数据进行两次异或操作后,将恢复这个对象原来的值。可用作加密工作。
(3) 按位与运算&用于选取一个数的某些二进制位。
(4) 按位异或运算|用于将一个数指定位置变成1。
(5) 按位异或运算^用于将一个数的指定位翻转,其它位不变。
(6) 对两个长度不等的数进行位运算时,右边对齐,短的数左边添0(整数/无符号数),或添1(负数)。
2. 字位移位运算符<<和>>
1) 调用格式:<数值> <<(左移位数)| <数值> >> (右移动位数)
2) 功能:将数值的二进制位数,向左或右移动指定位数。
3) 需要注意的问题:
(1) 对于整数或无符号数,左移运算中,若移掉的位中无1,则左移n位后的值是原值的2n倍。右移运算中,若移掉的位中无1,则右移n位后的值是原值的1/2n倍。
(2) 对于有符号数,在向左移时,得到的结果有可能是正数或负数;在向右移动时,分为逻辑右移和算术右移动,算术右移会保证数组的正负性不变。
10.4.2 与位运算有关的符合赋值运算符