10

我是按位运算符的新手。

我了解逻辑函数如何工作以获得最终结果。例如,当您对AND两个数字进行按位运算时,最终结果将是AND这两个数字的结果(1 & 0 = 0; 1 & 1 = 1; 0 & 0 = 0)。与ORXOR和相同NOT

我不明白的是他们的应用。我试着到处寻找,其中大多数只是解释按位运算的工作原理。在所有按位运算符中,我只了解移位运算符(乘法和除法)的应用。我也遇到了掩蔽。我知道屏蔽是使用按位完成的,AND但它的目的到底是什么,我在哪里以及如何使用它?

你能详细说明我如何使用遮罩吗?OR和有类似的用途XOR吗?

4

6 回答 6

19

位运算符的低级用例是执行以 2 为底的数学运算。有一个众所周知的技巧来测试一个数字是否是 2 的幂:

if ((x & (x - 1)) == 0) {
    printf("%d is a power of 2\n", x);
}

但是,它还可以提供更高级别的功能:集合操作。您可以将一组位视为一个集合。解释一下,让一个字节中的每个位代表 8 个不同的项目,比如说我们太阳系中的行星(冥王星不再被认为是行星,所以 8 位就足够了!):

#define Mercury (1 << 0)
#define Venus   (1 << 1)
#define Earth   (1 << 2)
#define Mars    (1 << 3)
#define Jupiter (1 << 4)
#define Saturn  (1 << 5)
#define Uranus  (1 << 6)
#define Neptune (1 << 7)

然后,我们可以形成一个行星集合(一个子集),比如使用|

unsigned char Giants = (Jupiter|Saturn|Uranus|Neptune);
unsigned char Visited = (Venus|Earth|Mars);
unsigned char BeyondTheBelt = (Jupiter|Saturn|Uranus|Neptune);
unsigned char All = (Mercury|Venus|Earth|Mars|Jupiter|Saturn|Uranus|Neptune);

现在,您可以使用 a&来测试两个集合是否有交集:

if (Visited & Giants) {
    puts("we might be giants");
}

^操作通常用于查看两个集合之间的不同之处(集合的并减去它们的交集):

if (Giants ^ BeyondTheBelt) {
    puts("there are non-giants out there");
}

因此,可以将其|视为并集、&交集以及并^集减去交集。

一旦您接受了位表示集合的想法,那么按位运算自然可以帮助操纵这些集合。

于 2013-06-19T01:21:55.080 回答
3

按位与的一种应用是检查一个字节中是否设置了单个位。这在网络通信中很有用,其中协议标头试图将尽可能多的信息打包到最小的区域中,以减少开销。

例如,IPv4 报头利用第 6 个字节的前 3 位来判断给定的 IP 数据包是否可以分段,如果可以,是否期望给定数据包的更多分段跟随。如果这些字段是整数(1 个字节)的大小,则每个 IP 数据包将比必要的大 21 位。这意味着每天通过互联网产生大量不必要的数据。

要检索这 3 个位,可以在位掩码旁边使用按位与来确定它们是否已设置。

char mymask = 0x80;
if(mymask & (ipheader + 48) == mymask)
  //the second bit of the 6th byte of the ip header is set 
于 2013-06-19T01:12:03.350 回答
3

小套装,如前所述。您可以快速执行大量的操作,交集和并集以及(对称)差异显然是微不足道的,但例如您也可以有效地:

  1. 得到集合中最低的项目x & -x
  2. 从集合中删除最低的项目x & (x - 1)
  3. 添加所有小于当前最小项目的项目
  4. 添加所有高于最小当前项目的项目
  5. 计算它们的基数(尽管算法很重要)
  6. 以某种方式排列集合,即更改项目的索引(并非所有排列都同样有效)
  7. 计算包含尽可能多项目的按字典顺序排列的下一组(Gosper's Hack)

图 1 和 2 及其变体可用于在小图上构建有效的图算法,例如参见计算机编程艺术 4A 中的算法 R。

按位运算的其他应用包括但不限于:

  • Bitboards,在许多棋盘游戏中都很重要。没有位板的国际象棋就像没有圣诞老人的圣诞节。它不仅是一种节省空间的表示,您还可以直接使用位板进行非平凡的计算(请参阅双曲线精粹)
  • 横向堆,以及它们在查找最近公共祖先和计算范围最小查询中的应用。
  • 高效循环检测(Gosper's Loop Detection,在 HAKMEM 中找到)
  • 向 Z 曲线地址添加偏移量而不解构和重建它们(参见 Tesseral Arithmetic)

这些用途更强大,但也先进、罕见且非常具体。然而,它们表明,按位运算不仅仅是过去低级时代遗留下来的可爱玩具。

于 2013-06-19T11:03:55.423 回答
1

位运算符在资源有限的系统中特别有用,因为每个位都可以编码一个布尔值。使用许多字符作为标志是浪费的,因为每个字符占用一个字节的空间(当它们每个可以存储 8 个标志时)。

通常,微控制器的 IO 端口具有 C 接口,其中每个位控制 8 个端口中的 1 个。如果没有位运算符,这些将很难控制。

关于屏蔽,通常同时使用 & 和 |:

x & 0x0F //ensures the 4 high bits are 0
x | 0x0F //ensures the 4 low bits are 1
于 2013-06-19T01:09:54.967 回答
1

示例 1

如果你有 10 个“一起工作”的布尔值,你可以大大简化你的代码。

int B1 = 0x01;
int B2 = 0x02;
int B10 = 0x0A;

int someValue = get_a_value_from_somewhere();

if (someValue & (B1 + B10)) {
    // B1 and B10 are set
}

示例 2

与硬件接口。硬件上的地址可能需要位级访问来控制接口。例如,缓冲区上的溢出位或状态字节可以告诉您 8 种不同事物的状态。使用位掩码,您可以获得所需的实际信息位。

if (register & 0x80) {
    // top bit in the byte is set which may have special meaning.
}

这实际上只是示例 1 的一个特例。

于 2013-06-19T01:05:59.003 回答
1

在微控制器应用程序中,您可以利用按位在端口之间切换。在下图中,如果我们想打开单个端口而关闭其余端口,则可以使用以下代码。

void main() 
{
     unsigned char ON = 1;
     TRISB=0;
     PORTB=0;
     while(1){
        PORTB = ON;
        delay_ms(200);
        ON = ON << 1;
        if(ON == 0) ON=1;
     }
}

在此处输入图像描述

于 2016-08-23T13:22:13.893 回答