zoukankan      html  css  js  c++  java
  • C++ 中的位操作符

    绪言

    我注意到一些人好像对位操作符不太清楚,所以我决定写篇简单的指南,说明如何使用他们。

    位简介

    bits,你会问他们是什么呢?
    其实,简单说,我们在电脑上处理各种工作都是由许多1和0完成的。我们在电脑上存储的所有数据都是用bits来表示的。一个byte是用8个bit表示的,一个WORD是用两个BYTE表示的,或者16个bit。一个DWORD是用两个WORD表示的,或者32个bit.

    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 -----------------------------+

    使用位操作符的美妙之处在于你可以把BYTE, WORD 或 DWORD 当成一个小型的数组或者结构。你可以使用位操作符检查单独的一个位的值,设置某一个或一组位的值。

    十六进制和他们与bits的关系

    使用bits时,很难只使用二元符号1或者0去表示每个数值。为了解决这个问题我们使用十六进制(基数是16)数。

    十六进制使用四个二进制位来表示从0到15的数字,这些数字也是单个的十六进制阿拉伯数字所能表示的范围。由四个二进制位或一个BYTE的一半组成的组被称为一个元组。一个BYTE包含两个元组,所以我们可以使用两个十六进制阿拉伯数字来表示一个BYTE类型的值。

    NIBBLE HEX VALUE
    ====== =========
    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

    因此,我们可以像下面这样用一个BYTE来表示字母'r'(ASCII码是114):

    0111 0010 binary
    7 2 hexadecimal

    我们可以把它写成'0x72'.

    二进制操作符

    有六个位操作符,他们是:

    & 与
    | 或
    ^ 异
    ~ 按位求反
    >> 右移
    << 左移

    & 与操作符

    &操作符比较两个数,只有要比较的两个值的相应位都被设置(为1-译者注)时,返回的值相应位才被设置。这些比较位使用下面的表进行比较:

    1 & 1 == 1
    1 & 0 == 0
    0 & 1 == 0
    0 & 0 == 0
    这个操作符理想的应用是建立一个掩码来检查某个位的值。假设我们有一个包含某些位标志的字节,我们想检查它的位4是否被设置(即是否被置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

    因此我们知道位4被置1了。

    | 操作符

    | 操作符比较两个数,只有他们相应位中的一个或两个同时被设置时,返回值相应位就会被设置。这些比较位使用下面的表进行比较:

    1 | 1 == 1
    1 | 0 == 1
    0 | 1 == 1
    0 | 0 == 0

    这个操作符的理想应用是确保某个位被设置。假设我们想某个值的位3一定被设置:

    BYTE b = 50;
    BYTE c = b | 0x04;
    cout << "c = " << c << endl;

    这会发生如下的计算:

    00110010 - b
    | 00000100 - | 0x04
    ----------
    00110110 - result

    ^操作符

    ^操作符比较两个数,只有这两个数的相应位标志不同时,返回数的相应位才会被设置。这些比较位使用下面的表进行比较:

    1 ^ 1 == 0
    1 ^ 0 == 1
    0 ^ 1 == 1
    0 ^ 0 == 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,而不管这个数据的大小。假设除了位0和位1,我们想把其他的位置1:

    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:

    BYTE b = 50;
    cout << "b = " << b << endl;
    BYTE c = b & ~0x10;
    cout << "c = " << c << endl;

    这会发生如下的计算:

    00110010 - b
    & 11101111 - ~0x10
    ----------
    00100010 - result

    >>和<<操作符

    >>(右移)和<<(左移)操作符按指定的位数移动位组。>>操作符将位组从高位向低位移。<<操作符将位组从低位向高位移。这两个操作符的一个应用是由于某些原因(如,检验MKEWPARAM, HIWORD, 和 LOWORD宏)需要对齐位组。

    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

    位域

    另一个可以使用位的有意思的事是使用位域。你可以使用位域在BYTE,WORD或DWORD内建立更小的结构。例如,假设我们想知道日期,但我们我想尽可能使用较少的内存。我们可以像下面这样建立数据结构:

    struct date_struct {
    BYTE day : 5, // 1 to 31
    month : 4, // 1 to 12
    year : 14; // 0 to 9999
    } date;

    在这个例子中,'日'占用了5个位,'月'占用了接下来的4位,同时'年'占用了接下来的14位。位24不用。如果我用整型定义每个域,这个结构将占用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 --+

    现在,注意它的声明部分,看我们做了什么。
    首先,我们看我们对位域结构使用的数据类型。这里我们用了BYTE。一个BYTE占8位,编译器将分配一个BYTE来存贮数据。如果在结构里我们使用了超过了8位的空间,编译器将分配另外的8位BYTE,直到能容纳我们的结构为止。如果我们使用了WORD或DWORD,编译器将分配一个总共32位的空间容纳我们的结构。
    现在,我们来看下不同的域是怎样声明的。首先,我们使用冒号分开域名和位数。既然我们能获得位域的地址,我们就能使用这个结构的地址。

    date.day = 12;

    dateptr = &date;
    dateptr->year = 1852;

  • 相关阅读:
    js获取触发事件的元素
    js获取触发事件的元素
    jQuery动态显示和隐藏datagrid中的某一列的方法
    jQuery动态显示和隐藏datagrid中的某一列的方法
    在控制器“xxxx”上找不到与该请求匹配的操作
    在控制器“xxxx”上找不到与该请求匹配的操作
    配置virtualbox使得openstackn controller可以连接外网
    配置mysql远程访问
    fuel部完高可用环境后vip__public无法启动从而导致创建虚机时无法创建virtualInterface.
    VMWare/VirtualBox三种网络模式及NAT/host-only模式设置上网与主机互联通信
  • 原文地址:https://www.cnblogs.com/daocaoren/p/2107768.html
Copyright © 2011-2022 走看看