快捷搜索:  汽车  科技

c语言里的位运算指令(CC语言10)

c语言里的位运算指令(CC语言10)信息的存取一般以字节为单位。实际上,有时存储一个信息不必用一个或多个字节,例如,“真”或“假”用0或1表示,只需1位即可。在计算机用于过程控制、参数检测或数据通信领域时,控制信息往往只占一个字节中的一个或几个二进制位,常常在一个字节中放几个信息。 1 与运算&相同位的两个数字都为1,则为1;若有一个不为1,则为0。位运算经常被用来创建、处理以及读取标志位序列——一种类似二进制的变量。虽然可以使用变量代替标志位序列,但是这样可以节省内存(1/32)。逻辑运算符和按位运算符之间的差别在于,按位运算符返回的并非布尔值,而是对操作数对应位执行指定运算的结果。C 让您能够执行按位NOT、OR、AND和XOR运算,它们分别使用~取反、使用|对相应位执行OR运算、使用&对相应位执行AND运算、使用^对相应位执行XOR运算。其中后三个运算符对变量与选择的数字(通常是位掩码)执行相应的运算

所有的数据均在计算机内部以位序列的形式表示。每一位可以为0或1。在大多数系统中,8位构成一个字节(byte),字节是字符型变量的标准存储单位。其他数据类型以更多的字节表达。

在C语言中,可以单独操控变量中的位。读者可能好奇,竟然有人想这样做。有时必须单独操控位,而且非常有用。例如,通常向硬件设备发送一两个字节来控制这些设备,其中的每个位(bit)都有特定的含义。另外,与文件相关的操作信息经常被储存,通过使用特定位表明特定项。许多压缩和加密操作都是直接处理单独的位。高级语言一般不会处理这些级别的细节,C在提供高级语言使得的同时,还能在为汇编语言所保留的级别上工作,这使其成为编写设备驱动程序和嵌入式代码的首选语言。

在硬件控制中,可以用位来表示特定的信息。位运算在编程硬件设备中非常有用,因为设备的状态经常表示为一系列单独的标志(也就是字节的每个位可以表示设备各个方面的状态),在需要把一组一关标志装入单个变量中时,按位运算也非常有用。

位运算符用来操纵整型操作数中的位。unsigned类型的整数通常使用位运算符来处理。

在实际的程序设计中,有时需要存储少量的信息,这些信息并不需要占用一个完整的字节,只需占用几个或一个二进制位。例如,在存入一个标志时,只有0和1两种状态,用一个二进制即可。如果给其分配一个字节的空间,便浪费了存储空间。因此,C 引入了位域这一数据类型。

逻辑运算符和按位运算符之间的差别在于,按位运算符返回的并非布尔值,而是对操作数对应位执行指定运算的结果。C 让您能够执行按位NOT、OR、AND和XOR运算,它们分别使用~取反、使用|对相应位执行OR运算、使用&对相应位执行AND运算、使用^对相应位执行XOR运算。其中后三个运算符对变量与选择的数字(通常是位掩码)执行相应的运算。

在整数的每位都表示特定标记的状态时,有些按位运算很有用,例如,32位的整数可用于表示32个布尔标记。

位运算的目的是用一个字节来表示更多的信息。如年份就可以使用一个字节来表示更多的信息,如用一个位来表示是31天还是30天?

位运算经常被用来创建、处理以及读取标志位序列——一种类似二进制的变量。虽然可以使用变量代替标志位序列,但是这样可以节省内存(1/32)。

信息的存取一般以字节为单位。实际上,有时存储一个信息不必用一个或多个字节,例如,“真”或“假”用0或1表示,只需1位即可。在计算机用于过程控制、参数检测或数据通信领域时,控制信息往往只占一个字节中的一个或几个二进制位,常常在一个字节中放几个信息。 1 与运算&

相同位的两个数字都为1,则为1;若有一个不为1,则为0。

与运算同与门电路相似,也类似于串联电路(1代表闭合)。

在程序中使用与运算可以进行清零操作。例如,将某个字节数据清零。可以将该字节的数据与0进行与运算,其结果将为0。因为任何数与0进行与运算,结果必然为0。此外,与运算还可以将一个字节中的某些位保留下来。例如,对于二进制数00001010,想要保留其1、3、5、7位,则可以将该二进制数与“01010101”二进制(该二进制数1、3、5、7位为1,其他位为0)进行与运算。

1.1 清零:00010010&0

1.2 保留某些位:如想保留00010010 如想保留1、3、5、7只,只需执行00010010&1010101

00010010&1的结果就是取二进制的最末位。这可以用来判断一个整数的奇偶,二进制的最末位为0表示该数为 偶数 ,最末位为1表示该数为奇数。

2 或运算 |

相同位只要一个为1即为1。

或运算通常用于二进制特定位上的无条件赋值,例如一个数 |1的结果就是把二进制最末位强行变成1。如果需要把二进制最末位变成0,对这个数 |1之后再减一就可以了,其实际意义就是把这个数强行变成最接近的偶数。

3 异或运算 ^

相同位不同则为1,相同则为0。

异或的符号是^。按位异或运算 对等长二进制模式按位或二进制数的每一位执行逻辑按位异或操作. 操作的结果是如果某位不同则该位为1 否则该位为0.

异或运算的逆运算是它本身,也就是说两次异或同一个数最后结果不变,即(a ^ b) ^ b = a。^运算可以用于简单的加密,比如我想对我MM说1314520,但怕别人知道,于是双方约定拿我的生日19880516作为密钥。1314520 xor 19880516 = 20665500,我就把20665500告诉MM。MM再次计算20665500 xor 19880516的值,得到1314520。

按位异或运算最经典的应用是不使用中间变量实现两个变量的互换。

int i = 5; int j = 4; i = i ^ j; j = i ^ j; i = i ^ j;

通过上述位运算后,i=4,j=5。

看运算的过程:

c语言里的位运算指令(CC语言10)(1)

也可以通过替换和交换来进行理解:

首先了解一下按位异或运算的在三个性质:

I 任何数据与0进行按位异或运算,结果仍为数据本身;

II 变量与自身进行接位异或运算,结果为0;

III 按位异或运算具有交换性,即a^b^c=a^c^b=b^a^c。

i = i ^ j; ① j = i ^ j; ② i = i ^ j; ③ ②,j=i^j = ①^j= i ^ j^j=j^0=i ③,i=i^j=i^②=i^i^j=①^①^j=0^j=j 4 取反运算 ~

取反运算的定义是把内存中的0和1全部取反。使用取反运算时要格外小心,你需要注意整数类型有没有符号。如果取反的对象是无符号整数(不能表示负数),那么得到的值就是它与该类型上界的差,因为无符号类型的数是用00到$FFFF依次表示的。

5 左移运算 <<

a << b就表示把a转为二进制后左移b位(在后面添b个0)。例如100的二进制为1100100,而110010000转成十进制是400,那么100 << 2 = 400。可以看出,a << b的值实际上就是a乘以2的b次方,因为在二进制数后添一个0就相当于该数乘以2。

通常认为a << 1比a * 2更快,因为前者是更底层一些的操作。因此程序中乘以2的操作请尽量用左移一位来代替。

定义一些常量可能会用到<<运算。你可以方便地用1 << 16 - 1来表示65535。很多算法和数据结构要求数据规模必须是2的幂,此时可以用<<来定义Max_N等常量。

6 右移运算 >>

和<<相似,a >> b表示二进制右移b位(去掉末b位),相当于a除以2的b次方(取整)。我们也经常用>> 1来代替div 2,比如二分查找、堆的插入操作等等。想办法用>>代替除法运算可以使程序效率大大提高。最大公约数的二进制算法用除以2操作来代替慢得出奇的mod运算,效率可以提高60%。

实例:

#include <iostream> #include <bitset> using namespace std; void main() { unsigned int a = 21; unsigned int b = 3; cout<<(bitset<8>)21<<endl; cout<<(bitset<8>)3<<endl; cout<<(bitset<8>)(a&b)<<endl; cout<<(bitset<8>)(a|b)<<endl; cout<<(bitset<8>)(a^b)<<endl; cout<<(bitset<8>)(~a)<<endl; cout<<(bitset<8>)(a<<b)<<endl; cout<<(bitset<8>)(a>>b)<<endl; system("pause"); } /* 00010101 00000011 00000001 00010111 00010110 11101010 10101000 00000010 */ 7 常用位运算实例

c语言里的位运算指令(CC语言10)(2)

8 位段或位域 ( bit field)

在开发应用程序时,有时需要在一个字节中表示多项内容。例如,在描述IP协议的首部时,其首部长度占4位(bit),版本号占4位。而程序最小的空间分配单位是一个字节,即8位。在定义描述IP协议首部的结构体时,该如何实现呢? C/C 语言提供了位域,允许用户单独访问一位数据。

怎样向一个字节中的一个或几个二进制位赋值和改变它的值呢?可以用以下两种方法:

可以人为地将一个整型变量data分为几部分。

但是用这种方法给一个字节中某几位赋值太麻烦。可以位段结构体的方法。

C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域” 。利用位段能够用较少的位数存储数据。

(1)位段成员的类型必须指定为unsigned或int类型。

(2) 若某一位段要从另一个字开始存放,可用以下形式定义:

unsigned a:1; unsigned b:2;一个存储单元 unsigned:0; unsigned c:3;另一存储单元

a、b、c应连续存放在一个存储单元中,由于用了长度为0的位段,其作用是使下一个位段从下一个存储单元开始存放。因此,只将a、b存储在一个存储单元中,c另存在下一个单元(“存储单元”可能是一个字节,也可能是2个字节,视不同的编译系统而异)。

(3) 一个位段必须存储在同一存储单元中,不能跨两个单元。如果第一个单元空间不能容纳下一个位段,则该空间不用,而从下一个单元起存放该位段。

(4) 可以定义无名位段。

(5) 位段的长度不能大于存储单元的长度,也不能定义位段数组。

(6) 位段可以用整型格式符输出。

(7) 位段可以在数值表达式中引用,它会被系统自动地转换成整型数。

实例:

#include <iostream> using namespace std; typedef struct { unsigned char a:3;/*0-2位*/ unsigned char b:2;/*3-4位*/ unsigned char c:3;/*5-7位*/ }Demo; void show1010(int val){ printf("%d : " val); for(int i=7;i>=0;i--){ //依次输出它的每一个二进制位 printf("%d" (val & 1<<i)!=0);//<<优先级高于& } printf("\n"); } int main() { Demo *tt; char buf[100]; memset(buf 0 100); tt = (Demo*)&buf[0]; tt->a = 5;//101 tt->b = 2;//10 tt->c = 7;//111 //一个字节值 printf("value:%d\n" buf[0]); //依次输出它的每一个二进制位 show1010(buf[0]); //当前一个字节值为: -11 //1 1 1 1 0 1 0 1 //1 1 1 1 0 1 0 1 // 7 2 5 // c b a // 要得到a值,即左边5个位与0做与运算 // 与运算 //-11 :1 1 1 1 0 1 0 1 =》 -11 //0x07 :0 0 0 0 0 1 1 1 =》 0x07 //与结果 :0 0 0 0 0 1 0 1 int a = buf[0] & 0x07; //依次输出它的每一个二进制位 show1010(a); //-11 :1 1 1 1 0 1 0 1 =》 -11 //0x07 :0 0 0 1 1 0 0 0 =》 0x18 //与结果 :0 0 0 1 0 0 0 0 // 向右边移动3位 int b = (buf[0] & 0x18) >> 3; //依次输出它的每一个二进制位 show1010(b); printf("a:%d\n" a); printf("b:%d\n" b); system("pause"); } /* value:-11 -11 : 11110101 5 : 00000101 2 : 00000010 a:5 b:2 */

关于位运算的一些操作,可见:

C|位运算到底能干啥?标志位的诸多应用场景

-End-

猜您喜欢: