C/C++位操作简介

C/C++位操作简介


前言
我注意到有些人对位运算感到困惑,所以我决定写这篇简单的教程来说明位运算如何操作。

位简介
位,它是什么?你可能会问。

简单来说,位就是1和0,在电脑中做的每一件事都是由它们组成的。电脑中所有的数据使用的是位。一个字节由8个位组成;一个字由两个字节组成,即16个位;而一个双字由四个字节组成,即32个位。

 0 1 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 1 1 1 0 1 0 0 0 1 1 1 1 0 0 0
||              |               |               |              ||
|+- bit 31      |               |               |       bit 0 -+|
|               |               |               |               |
+-- BYTE 3 -----+--- BYTE 2 ----+--- BYTE 1 ----+-- BYTE 0 -----+
|                               |                               |
+----------- WORD 1 ------------+----------- WORD 0 ------------+
|                                                               |
+--------------------------- DWORD -----------------------------+

使用字节,字或者双字来进行位操作显得比较美观,就像使用一个小型数组或结构。使用位运算,可以检查或设置单独某一位的值或组位的值。

十六进制数和位的关系
人们发现,使用二进制计数法表示数字比较的困难。为避免这一问题,采用十六进制计数法(基数为16)。

十六进制的一位数字从0到15分别用二进制的四位来表示。四位一组,即半字节。一个字节有两个半字节,则可以用两位十六进制数表示一个字节的值。

半字节     十六进制数
======   =========
 0000        0
 0001        1
 0010        2
 0011        3
 0100        4
 0101        5
 0110        6
 0111        7
 1000        8
 1001        9
 1010        A
 1011        B
 1100        C
 1101        D
 1110        E
 1111        F

如果有一个字节,内容为字母‘r’(ASCII 码 114),则表示如下:

0111 0010 二进制数
  7    2  十六进制数
记为:0x72

 

位运算符

共有6种位运算符,如下:
  &  与运算符
  |  或运算符
  ^  异或运算符
  ~  取反运算符
 >>  右移运算符
 <<  左移运算符


&  运算符
&(与)运算要求有两个运算值,然后返回一个值,当且仅当两个运算值都位1时,返回值为1。如下表:
   1   &   1   ==   1
   1   &   0   ==   0
   0   &   1   ==   0
   0   &   0   ==   0
  
一个字节可以包含位标志,而使用与运算可以通过设置掩码来检查某位的值。算法如下:它用来判断字节中的第四位是否为1

BYTE b = 50;
if ( b & 0x10 )
    cout << "Bit four is set" << endl;
else
    cout << "Bit four is clear" << endl;
通过以下计算可以得到结果:
    00110010  - b
  & 00010000  - & 0x10
  ----------
    00010000  - result
所以,第四位为1。


| 运算符
|(或)运算符要求两个运算值,然后返回一个值,当且仅当两个运算值中有一个为1或都为1时,返回值为1。如下表:
   1   |   1   ==   1
   1   |   0   ==   1
   0   |   1   ==   1
   0   |   0   ==   0
  
使用或运算可以保证字节中的某位为1。算法如下:它用来保证第二位总是为1

BYTE b = 50;
BYTE c = b | 0x04;
cout << "c = " << c << endl;
通过以下计算可以得到结果:
    00110010  - b
  | 00000100  - | 0x04
  ----------
    00110110  - result
   
   
^ 异或运算符
^ (异或)运算符要求有两个运算值,然后返回一个值,当且仅当两个运算值中有一个为1但不同时为1时,返回值为1。如下表:
   1   ^   1   ==   0
   1   ^   0   ==   1
   0   ^   1   ==   1
   0   ^   0   ==   0
  
使用异或运算可以翻转特定的位。即0变1,1变0。算法如下:翻转第三和第四位
BYTE b = 50;
cout << "b = " << b << endl;
b = b ^ 0x18;
cout << "b = " << b << endl;
b = b ^ 0x18;
cout << "b = " << b << endl;
通过以下计算可以得到结果:
    00110010  - b
  ^ 00011000  - ^ 0x18
  ----------
    00101010  - result
   
    00101010  - b
  ^ 00011000  - ^ 0x18
  ----------
    00110010  - result
   
   
~ 取反运算符
~(取反)运算符只要求一个运算值,然后将所有的1变成0,所有的0变成1。使用取反运算可以将某些字节置0,确保其它字节置1,而不用考虑数据的大小。算法如下:将所有位置1,而第一和第零位置0
BYTE b = ~0x03;
cout << "b = " << b << endl;
WORD w = ~0x03;
cout << "w = " << w << endl;
通过以下计算可以得到结果:
    00000011  - 0x03
    11111100  - ~0x03  b
    0000000000000011  - 0x03
    1111111111111100  - ~0x03  w
   
同&(与)运算符一起使用,可以使任意位置0。算法如下:将第四位置0
BYTE b = 50;
cout << "b = " << b << endl;
BYTE c = b & ~0x10;
cout << "c = " << c << endl;
通过以下计算可以得到结果:
    00110010  - b
  & 11101111  - ~0x10
  ----------
    00100010  - result
   
   
>>和<< 右移和左移运算符
>>(右移)运算符和<<(左移)运算符将数据右移或左移若干位。>>右移运算从高位往低位移,<<左移运算从低位往高位移。
BYTE b = 12;
cout << "b = " << b << endl;
BYTE c = b << 2;
cout << "c = " << c << endl;
c = b >> 2;
cout << "c = " << c << endl;
通过以下计算可以得到结果:
    00001100  - b
    00110000  - b << 2
    00000011  - b >> 2
   
   
位段
位段是位运算中比较令人感兴趣的部分。使用位段可以在一个字节,字或双字内设置小型结构。例如:要记录日期,要求尽可能少的使用内存,则可以采用如下的结构申明:
struct date_struct {
    BYTE day   : 5,   // 1 to 31
         month : 4,   // 1 to 12
         year  : 14;  // 0 to 9999
    } date;
   
在上面的例子中,日占据了最低的5位,月份占据了接下来的4位,年份为接下来的14位。则整个日期储存在三个字节的23位中。第二十四位被忽略。如果使用整形申明则将占据12个字节。
|0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0|
  |                           |       |         |
  +------ year ---------------+ month +-- day --+
 
如上所述,date类型使用的位段结构。这里使用的是BYTE。一BYTE为8位,使用的时候,编译器将申请一个BYTE来储存。如果结构超过8位,编译器将再申请一个BYTE,直到足够用来储存结构。如果使用字或双字,编译器将总共申请32位用来储存结构。

怎样申明位段?首先申明位段变量,跟着冒号,然后是分配给变量的位数;每位段用逗号分隔,最后用分号表示申明结束。

完成结构申明后,则可以通过存取标记方便的使用结构,同时也可以使用地址操作符使用结构的地址对结构进行操作。如下:

date.day = 12;
dateptr = &date;
dateptr->year = 1852;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值