C语言基础(十)编译预处理与位运算


系列合集 初窥C语言

十一、编译预处理与位运算

预处理命令:宏定义,文件包含处理,条件编译
预处理命令概述:以“#”号开头的预处理命令。如包含命令#include,宏定义命令#define等。在源程序中这些命令都放在函数之外,而且一般放在源文件的前面,它们称为预处理部分。

11.1 宏定义

11.1.1 无参宏定义

无参宏的宏名后不带参数。
其定义的一般形式为

#define 标识符 字符串
//例如
#define PI 3.14159

其中#表示这是一条预处理命令。
define为宏定义命令
标识符为所定义的宏名
字符串可以是常数,表达式,格式串等

无参数宏定义说明:
1)宏定义是用宏名来表示一个字符串。
2)宏名一般习惯用大写字母表示,以便与变量名相区别。但这并非规定,用小写字母语法上不算错误。
3)在宏展开时以该字符串取代宏名,这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不做检查。
4)宏定义不是说明或语句,在行末不必加分号,如果加上分号则把分号算在字符串内。
5)#define命令出现在程序中函数的外面,宏名的有效范围为定义命令之后到当前源文件结束,通常#define写在文件开头。
6)宏定义必须写在函数外面,如果要终止其作用域,可使用#undef命令
7)在进行宏定义时,可以引用已定义的宏名。
8)对程序中引用’’ ''括起来的字符串,即使与宏名相同,也不进行置换。
9)宏定义是专门用于预处理命令的一个专门的名词,它与定义变量的含义不同,只做字符替换,不分配内存空间。

11.1.2 带参宏定义

对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代替形参。
带参宏定义的一般形式:

#define 宏名(参数表) 字符串
//例如
#define S(a,b) a*b
area = S(2,3);
//带参宏调用的一般形式为:宏名(实参表)

带参宏定义的说明:
1)对带参数的宏展开只是语句中的宏名后面括号内的实参字符串代替#define命令行中的形参。
2)在宏定义时,宏名和形参表之间不能有空格出现。
3)对函数中的实参和形参都要定义类型,二者的类型要求一致,如不一致,应进行类型转换。而宏不存在类型问题,宏名无类型,它的参数也无类型,只是一个符号代表,展开时代入指定的字符串即可,宏定义时,字符串可以是任意类型的数据。
4)调用函数只可得到一个返回值,而宏可以设法得到几个结果。
5)使用宏次数多时,宏展开后源程序长,因为每展开一次都使程序增长,而函数调用不使源程序变长。
6)宏替换不占运行时间,只占编译时间,而函数调用则占运行时间(分配单元,保留现场,值传递,返回)

11.2 文件包含处理

文件包含命令行的一般形式为:

#include<文件名> // "文件名"也可以
#include<stdio.h>
#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声明。

11.3 条件编译

预处理程序提供了条件编译的功能。可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。这对程序的移植和调试有很大作用。
条件编译三种形式:
(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.

11.5 位运算符举例

循环移位
要求将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 <stdio.h>
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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rpk712

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值