- 十一、编译预处理与位运算
- 11.1 宏定义
- 11.1.1 无参宏定义
- 11.1.2 带参宏定义
- 11.2 文件包含处理
- 11.3 条件编译
- 11.4 位运算符与位运算
- &
- |
- ^
- ~
- <<
- >>
- 11.5 位运算符举例
系列合集 初窥C语言 十一、编译预处理与位运算
预处理命令:宏定义,文件包含处理,条件编译
预处理命令概述:以“#”号开头的预处理命令。如包含命令#include,宏定义命令#define等。在源程序中这些命令都放在函数之外,而且一般放在源文件的前面,它们称为预处理部分。
无参宏的宏名后不带参数。
其定义的一般形式为
#define 标识符 字符串
//例如
#define PI 3.14159
其中#表示这是一条预处理命令。
define为宏定义命令
标识符为所定义的宏名
字符串可以是常数,表达式,格式串等
无参数宏定义说明:
1)宏定义是用宏名来表示一个字符串。
2)宏名一般习惯用大写字母表示,以便与变量名相区别。但这并非规定,用小写字母语法上不算错误。
3)在宏展开时以该字符串取代宏名,这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不做检查。
4)宏定义不是说明或语句,在行末不必加分号,如果加上分号则把分号算在字符串内。
5)#define命令出现在程序中函数的外面,宏名的有效范围为定义命令之后到当前源文件结束,通常#define写在文件开头。
6)宏定义必须写在函数外面,如果要终止其作用域,可使用#undef命令
7)在进行宏定义时,可以引用已定义的宏名。
8)对程序中引用’’ ''括起来的字符串,即使与宏名相同,也不进行置换。
9)宏定义是专门用于预处理命令的一个专门的名词,它与定义变量的含义不同,只做字符替换,不分配内存空间。
对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代替形参。
带参宏定义的一般形式:
#define 宏名(参数表) 字符串
//例如
#define S(a,b) a*b
area = S(2,3);
//带参宏调用的一般形式为:宏名(实参表)
带参宏定义的说明:
1)对带参数的宏展开只是语句中的宏名后面括号内的实参字符串代替#define命令行中的形参。
2)在宏定义时,宏名和形参表之间不能有空格出现。
3)对函数中的实参和形参都要定义类型,二者的类型要求一致,如不一致,应进行类型转换。而宏不存在类型问题,宏名无类型,它的参数也无类型,只是一个符号代表,展开时代入指定的字符串即可,宏定义时,字符串可以是任意类型的数据。
4)调用函数只可得到一个返回值,而宏可以设法得到几个结果。
5)使用宏次数多时,宏展开后源程序长,因为每展开一次都使程序增长,而函数调用不使源程序变长。
6)宏替换不占运行时间,只占编译时间,而函数调用则占运行时间(分配单元,保留现场,值传递,返回)
文件包含命令行的一般形式为:
#include<文件名> // "文件名"也可以
#include
#include "math.h"
说明:
1)一个include命令只能指定一个被包含的文件,如果要包含n个文件,要用n个#include命令。
2)如果文件1包含文件2,而文件2中要用到文件3的内容,则可在文件1中用两个include命令分别包含文件2和文件3,而文件3应出现在文件2之前。
即在file1.c中定义:
#include "file3.h"
#include "file2.h"
3)在一个被包含文件中又可以包含另一个被包含文件,即文件包含是可以嵌套的。
4)被包含文件(file2.h)与其所在文件(即用#include命令的源文件file1.c)在预编译后成为同一个文件,因此,如果file2.h中有全局静态变量,它也在file1.c中有效,不必用extern声明。
预处理程序提供了条件编译的功能。可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。这对程序的移植和调试有很大作用。
条件编译三种形式:
(1)第一种形式
#ifdef 标识符
程序段1;
#else
程序段2;
#endif
它的功能是:如果标识符已被#define命令定义过则对程序段1进行编译;否则对程序段2进行编译。如果没有程序段2(它为空),本格式中的#else也可以不写:
#ifdef 标识符
程序段;
#endif
(2)第二种形式
#ifndef 标识符
程序段1;
#else
程序段2;
#endif
与第一种形式的区别是“ifdef”改为“ifndef”。
作用:果标识符未被#define命令定义过则对程序段1进行编译;否则对程序段2进行编译。与第一种形式的功能正好相反。
(3)第三种形式
#if 常量表达式
程序段1;
#else
程序段2;
#endif
它的功能是,如常量表达式的值为真(非0),则对程序段1进行编译,否则对程序段2进行编译。
11.4 位运算符与位运算位运算是指进行二进制位的运算。
C语言提供的位运算符如下:
运算符 | 含义 |
---|---|
& | 按位与 |
| | 按位或 |
^ | 按位异或 |
~ | 取反 |
<< | 左移 |
>> | 右移 |
&:按位与。参加运算的两个数据,按二进制进行“与”运算。如果两个相应的二进制都为1,则该位的结果为1,否则为0。即:0&0 = 0;0&1 = 0;1&0 = 0; 1&1 = 1
|| :按位或。两个相应的二进位中只要有一个为1,该位的结果值为1.
即 0|0 = 0;0|1 = 1;1|1 = 1;
举例:060|017 八进制60和八进制17进行按位或运算
00110000
00001111
00111111
如果想一个数a的低四位全改为1,只需将a与017进行按位或 *** 作即可
^:异或。异或运算符也称XOR运算符。它的规则是若参加运算的两个二进制同号,则结果为0(假);异号则为1(真)。即0^0 = 0;0^1 = 1;1^0 = 1;1^1 = 0.
例如:
00111001(十进制数57,八进制数071)
00101010(十进制数42,八进制数052)
00010011(十进制数19,八进制数023)
“异或”的意思是判断两个相应的位值是否为“异”,为“异”(值不同)就取真(1),否则为假(0)。
用途:
1)使特定位翻转
例:X=10101110,使X低4位翻转,用X ^ 0000 1111 = 1010 0001即可得到
2)0相^,保留原值
X ^ 0000 0000 = 1010 1110
~:取反。是一个单目运算符,用来对一个二进制数按位取反,即吧0变成1,把1变成0.
<<<<:左移运算符,用来将一个数的各位全部左移若干位。
如: a << 2 将a的二进制左移2位,右补0.
若a = 15 (二进制数00001111),左移两位为00111100,即十进制数60。
高位左移后溢出,舍弃。
>>:右移运算符,与左移运算符相反,无符号数高位补0,负数高位补1
右移一位相当于除以2,右移n位相当于除以2n
不同长度的数据进行位运算
如果两个数据长度不同(例如long和int型数据)进行位运算时(如a&b,a为long型,b为int型),系统会将二者按右端对齐。如果b为正数,则左侧16位补满0.若b为负数,左端补满1.如果b为无符号数,左端补满0.
循环移位
要求将a进行右循环移位。将a右循环移动n位,将a中原来左面(16-n)位右移n位,原来右端n位移到左端n位。假设用两个字节存放一个整数。
步骤:
1)将a的右端n位先放到b中的高n位内, b = a << (16 - n)
2)将a右移n位,其左面高位n位补0, c = a >> n
3)将c与b进行按位或运算, c = c | b
#include
int main()
{
unsigned a,b,c;
int n;
scanf("a=%o,n=%d",&a,&n);
b = a << (16 - n);
c = a >> n;
c = c | b;
printf("%o\n%o",a,c);
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)