逻辑运算符 位运算符_现实世界中按位运算符的使用

逻辑运算符 位运算符

As a developer, you definitely would’ve learned about bitwise operators at some point and would’ve thought to yourself. “huh, that’s neat. but where would I actually use it?”. That’s what we’ll be looking at today.

作为开发人员,您一定会在某个时候了解过按位运算符,并且会对自己进行思考。 “嗯,很整洁。 但我实际在哪里使用呢?”。 这就是我们今天要看的。

The modern world of computing and software engineering has extracted the complexities away to an extent that you really don’t need to know about how the code you write is executed.

在现代的计算和软件工程世界中,您已经完全不必了解编写代码的执行方式,从而将复杂性去除了。

While this is okay for most uses where your code is executed on servers and powerful computers, you’ll run into major problems while working on limited resource devices like microcontrollers and FPGAs. You will need to know how your code is executed on these devices in order to optimize it to run on limited resources. (Kilobytes of RAM & KHz of clock speed).

尽管对于大多数在服务器和功能强大的计算机上执行代码的应用而言,这是可以的,但在使用有限资源的设备(例如微控制器和FPGA)时,您会遇到重大问题。 您将需要知道您的代码如何在这些设备上执行,以便对其进行优化以在有限的资源上运行。 (千字节RAM和时钟速度千赫)。

Image for post
ESP32 Development Board
ESP32开发板

Understanding bitwise operators will bring you a step closer to optimization. Let’s first understand what bitwise operators are. Please note that this article will cover usage of bitwise operators in C, but the logic and syntax remains common across most languages. It’s also expected that you know and understand binary to decimal and decimal to binary conversions. If you don’t know that already, there are plenty resources available online for you to learn it.

了解按位运算符将使您更接近优化。 首先让我们了解什么是按位运算符。 请注意,本文将介绍C语言中按位运算符的用法,但是逻辑和语法在大多数语言中仍然通用。 还期望您了解并理解二进制到十进制以及十进制到二进制的转换。 如果您还不知道,则可以在网上找到大量资源供您学习。

目录 (Table of Contents)

  1. What are bitwise Operators?

    什么是按位运算符?

  2. The Different Bitwise Operators and Their Functions

    不同的按位运算符及其功能

  3. Practical Use of Bitwise Operators

    逐位运算符的实际使用

    Practical Use of Bitwise Operators (If you already know bitwise operators you can jump straight to this section)

    按位运算符的实际使用 (如果您已经知道按位运算符,则可以直接跳到本节)

什么是按位运算符? (What are bitwise Operators?)

Bitwise operators are operators that perform operations on data at a bit level.

按位运算符是按位级别对数据执行运算的运算符。

What do I mean by that? They help you manipulate the bits that make up the piece of data which is represented by a datatype. This will start making more sense when we dive deeper into the topic. Let’s now take a look at what are the different bitwise operators available to us.

那是什么意思 它们可帮助您操纵组成数据类型的位,这些数据由数据类型表示。 当我们深入探讨该主题时,这将变得更加有意义。 现在让我们看一下我们可以使用哪些不同的按位运算符。

╔═════════════════╦═══════════════╦══════════════╦══════════╦═══════════════╗
║ operator        ║ name          ║ example      ║ variant  ║ associativity ║
╠═════════════════╬═══════════════╬══════════════╬══════════╬═══════════════╣
║ ~               ║ bitwise NOT   ║ ~5           ║ -        ║ Right to left ║
╠═════════════════╬═══════════════╬══════════════╬══════════╬═══════════════╣
║ >>              ║ Right Shift   ║ 5 >> 2       ║ x >>= 2  ║ Left to right ║
╠═════════════════╬═══════════════╬══════════════╬══════════╬═══════════════╣
║ <<              ║ Left Shift    ║ 5 << 2       ║ x <<= 2  ║ Left to right ║
╠═════════════════╬═══════════════╬══════════════╬══════════╬═══════════════╣
║ &               ║ bitwise AND   ║ 5 & 6        ║ x &= 6   ║ Left to right ║
╠═════════════════╬═══════════════╬══════════════╬══════════╬═══════════════╣
║ ^               ║ bitwise XOR   ║ 5 ^ 6        ║ x ^= 6   ║ Left to right ║
╠═════════════════╬═══════════════╬══════════════╬══════════╬═══════════════╣
║ |               ║ bitwise OR    ║ 5 | 6        ║ x |= 6   ║ Left to right ║
╚═════════════════╩═══════════════╩══════════════╩══════════╩═══════════════╝

Using the sizeof() operator, we can find the number of bytes each datatype represents and since each byte it made up of 8 bits, we can find out the total number of bits taken up by each datatype with some multiplication magic.

使用sizeof()运算符,我们可以找到每个数据类型表示的字节数,并且由于每个字节都由8位组成,因此我们可以通过某种乘法运算来找出每个数据类型占用的总位数。

Image for post

Let’s now take a look at each operator in detail.

现在让我们详细了解每个运算符。

不同的按位运算符及其功能 (Different Bitwise Operators and Their Functions)

&按位与 (& Bitwise AND)

The bitwise AND operator (&) takes two operands and compares the operands bit by bit and sets the corresponding output bit to 1 if and only if both input bits are 1. Here’s the truth table for the bitwise AND operator:

当且仅当两个输入位均为1时,按位AND运算符(&)接受两个操作数并逐位比较这些操作数,并将相应的输出位设置为1。这是按位AND运算符的真值表:

╔═══════════════╦══════════════╦════════════╗
║ input bit(1)  ║ input bit(2) ║ output bit ║
╠═══════════════╬══════════════╬════════════╣
║ 1             ║ 1            ║ 1          ║
╠═══════════════╬══════════════╬════════════╣
║ 1             ║ 0            ║ 0          ║
╠═══════════════╬══════════════╬════════════╣
║ 0             ║ 1            ║ 0          ║
╠═══════════════╬══════════════╬════════════╣
║ 0             ║ 0            ║ 0          ║
╚═══════════════╩══════════════╩════════════╝

Let’s take the following code for example

让我们以以下代码为例

#include <stdio.h>
#include <stdlib.h>


int main(){
  int x=5, y=6;
  printf("x & y = %d\n", (x & y));
  // "x & y = 4"
  return EXIT_SUCCESS;
}

so the value of the variable x is 5 and the value of variable y is 6. The computer that I’m working on is a 64-bit machine so the sizeof(int) is 4 bytes so the binary representation of 5 world be:00000000 00000000 00000000 00000101

因此变量x的值为5,变量y的值为6。我正在使用的计算机是一台64位计算机,因此sizeof(int)为4字节,因此5世界的二进制表示为: 00000000 00000000 00000000 00000101

and the binary representation of 6 would be:00000000 00000000 00000000 00000110

并且6的二进制表示形式是: 00000000 00000000 00000000 00000110

For the sake of simplicity, let’s just take the last byte into consideration and let’s perform the bitwise AND operation on them.

为了简单起见,我们只考虑最后一个字节,然后对它们执行按位与运算。

00000101 & 00000110 = 00000100 // 4

As you can see, the bits of 5 & 6 were compared and the output byte was set to 00000100 as a result which is the binary representation of the number 4. This is how we arrived at “5 & 6 = 4”.

如您所见,比较了5和6的位,结果将输出字节设置为00000100 ,这是数字4的二进制表示。这就是我们得出“ 5&6 = 4”的方式。

| 按位或 (| Bitwise OR)

The bitwise OR operator take in two operands just like the bitwise AND and compares them bit by bit but instead of setting the output bit to 1 only when both input bits are 1, it follows this truth table:

与按位AND一样,按位OR运算符采用两个操作数,并逐位比较它们,但不是仅当两个输入位均为1时才将输出位设置为1,而是遵循此真值表:

╔═══════════════╦══════════════╦════════════╗
║ input bit(1)  ║ input bit(2) ║ output bit ║
╠═══════════════╬══════════════╬════════════╣
║ 1             ║ 1            ║ 1          ║
╠═══════════════╬══════════════╬════════════╣
║ 1             ║ 0            ║ 1          ║
╠═══════════════╬══════════════╬════════════╣
║ 0             ║ 1            ║ 1          ║
╠═══════════════╬══════════════╬════════════╣
║ 0             ║ 0            ║ 0          ║
╚═══════════════╩══════════════╩════════════╝

Let’s look at the below example:

让我们看下面的例子:

#include <stdlib.h>
#include <stdio.h>


void main(){
  printf("5 | 6 = %d\n", (5 | 6));
  return EXIT_SUCCESS;
}

The above program would print “5 | 6 = 7”. The following bit view of the operation would tell you how the program arrived at that output:

上面的程序将显示“ 5 | 6 = 7英寸 。 下面的操作位视图将告诉您程序如何到达该输出:

00000101 & 00000110 = 00000111 // 7

^按位XOR (^ Bitwise XOR)

The bitwise XOR operator works similar to the bitwise OR operator. The only difference is that the output bit will be set to 1 when both input bits are different. The following is the logic table for bitwise XOR:

按位XOR运算符的工作方式类似于按位OR运算符。 唯一的区别是,当两个输入位都不相同时,输出位将设置为1。 以下是按位XOR的逻辑表:

╔═══════════════╦══════════════╦════════════╗
║ input bit(1)  ║ input bit(2) ║ output bit ║
╠═══════════════╬══════════════╬════════════╣
║ 1             ║ 1            ║ 0          ║
╠═══════════════╬══════════════╬════════════╣
║ 1             ║ 0            ║ 1          ║
╠═══════════════╬══════════════╬════════════╣
║ 0             ║ 1            ║ 1          ║
╠═══════════════╬══════════════╬════════════╣
║ 0             ║ 0            ║ 0          ║
╚═══════════════╩══════════════╩════════════╝

Here is an example program that would show how the bitwise XOR works:

这是一个示例程序,将显示按位XOR的工作方式:

#include <stdlib.h>
#include <stdio.h>


int main(){
  printf("5^6 = %d\n", (5^6));
  return EXIT_SUCCESS;
}

The above program would generate the following output:

上面的程序将产生以下输出:

5^6 = 3

How you ask? let’s take a look at the bits

你怎么问 让我们来看看

00000101 & 00000110 = 00000011 // 3

Comparing the input bits according to the bitwise XOR truth table. We arrive at 00000011 which is the binary representation of 3.

根据按位XOR真值表比较输入位。 我们到达00000011 ,它是3的二进制表示形式。

〜按位非 (~ Bitwise NOT)

The bitwise NOT operator is a little different from the other operators as it accepts only one operand. The bitwise NOT operator flips the bits of it’s operand. Let us take a look at the following example to understand it better,

按位NOT运算符与其他运算符略有不同,因为它仅接受一个操作数。 按位NOT运算符将其操作数的位翻转。 让我们看下面的例子,以更好地理解它,

#include <stdlib.h>
#include <stdio.h>


int main(){
  printf("~5=%d\n", ~5);
  return EXIT_SUCCESS;
}

The above program would produce “~5=-6” as output. Let’s look at how the program arrived at the value of -6 when the bitwise NOT operator was applied to 5.

上面的程序将产生“〜5 = -6”作为输出。 让我们看一下将按位NOT运算符应用于5时程序如何达到-6的值。

~00000101 = 11111010 // -6

Since negative numbers are stored as 2’s complement, “11111010” would be -6 in it’s decimal representation.

由于负数存储为2的补数,因此“ 11111010”的十进制表示形式将为-6。

<<按位左移运算符 (<< Bitwise Left Shift Operator)

The left shift operator like the name suggests, moves the bits in it’s left operand to the right by the number of places specified in the right operand.

顾名思义,左移位运算符将其左操作数中的位向右移动右操作数中指定的位数。

So, if the binary representation of 5 is 00000101 then applying the shift operator like so “5 << 2” would move the bits to the left by two places striping the 2 most significant bits and adding 2 zeros as the least significant bits. The binary representation of the result would be 00010100 which is 20 in decimal representation. Let’s take a look this in code.

因此,如果5的二进制表示形式是00000101,则应用移位运算符(如“ 5 << 2”)会将这些位向左移动两个位置,以剥离2个最高有效位并添加2个零作为最低有效位。 结果的二进制表示形式为00010100,十进制表示形式为20。 让我们看看代码中的内容。

As long as your set bits don’t overflow and get dropped off, each left shift by 1 place has the effect of multiplying the left operand by 2.

只要您的设置位不会溢出并掉落,每个左移1位的作用就是将左操作数乘以2。

#include <stdio.h>
#include <stdlib.h>


int main(){
  printf("5 << 2 = %d\n",(5 << 2));
  return EXIT_SUCCESS;
}

As expected, the program prints an output of “5 << 2 = 20”.

如预期的那样,程序将输出“ 5 << 2 = 20”。

>>按位右移运算符 (>> Bitwise Right Shift Operator)

The bitwise right shift operator is very similar to the left shift operator. Instead of shifting the bits to the left, believe it or not, it shifts the bits to the right. Yes, you didn’t see that one coming did you?

按位右移运算符与左移运算符非常相似。 不管您信不信由你,而不是将位向左移动,而是将位向右移动。 是的,您没有看到一个人来吗?

Image for post

As the bits are shifted to the right, the least significant bits are dropped and an equivalent number of zeros are added as most significant bits.

当这些位向右移动时,最低有效位将被丢弃,等价的零被添加为最高有效位。

Here’s some code.

这是一些代码。

#include <stdlib.h>
#include <stdio.h>


int main(){
  printf("5 >> 2 = %d\n", (5 >> 2));
  // 00000101 >> 2 == 00000001
  return EXIT_SUCCESS;
}

Simple enough, when we shift the bits of 5 (00000101) 2 places to the right, we get (00000001) which is basically 1 in decimal and binary which is why the above code would print “5 >> 2 = 1”.

很简单,当我们将5(00000101)的两位右移时,我们得到的(00000001)基本上是十进制和二进制的1,这就是为什么上面的代码将打印“ 5 >> 2 = 1”的原因。

As long as your set bits don’t overflow and get dropped off, each right shift by 1 place has the effect of dividing the left operand by 2.

只要您的设置位不会溢出并掉落,将每个右移1位就可以将左操作数除以2。

Now that you’ve finally mastered bitwise operators, you’re probably thinking “What on earth would I do with this abundance of information?”.

既然您终于掌握了按位运算符,您可能会想:“到底,我将如何利用这些丰富的信息呢?”。

I have got you covered. In the next section, we will discuss some of the practical uses of bitwise operators.

我已经覆盖了你。 在下一节中,我们将讨论按位运算符的一些实际用法。

按位运算符的实际用法 (Practical Uses of Bitwise Operators)

1.存储多个布尔标志 (1. Storing Multiple Boolean Flags)

When working on limited memory devices, you can’t really afford to have a thousand boolean flags assigned to variables. This is a really inefficient way to store your flags because Boolean values in C are basically a byte long. We don’t need an entire byte to store just a zero or a one. The rest of the bits are going to remain zero throughout. You cannot afford to throw away precious memory when you’re working with just KBs of RAM.

在有限的内存设备上工作时,您真的负担不起为变量分配一千个布尔标志。 这是一种存储标志的效率极低的方法,因为C语言中的布尔值基本上是一个字节长。 我们不需要整个字节来存储零或一。 其余位将始终保持零。 当您仅使用KB KB的RAM时,您将无法丢弃宝贵的内存。

So what is the solution? Let’s look at the code.

那么解决方案是什么? 让我们看一下代码。

#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>


#define SECURITY_CHECK_FLAG  0b00000001
#define TWO_FACTOR_AUTH_FLAG 0b00000010
#define JUST_A_FLAG_FLAG     0b00000100


int main(){
  // THE BULKY WAY
  // MEMORY CONSUMED: 3 * sizeof(bool) = 3 bytes
  bool security_check   = true;
  bool two_factor_auth  = false;
  bool just_a_flag_flag = true;




  // THE OPTIMIZED BITWISE WAY
  // MEMORY CONSUMED: sizeof(unsigned short int) =  2 bytes


  unsigned short int flags = 0;             /* 0b00000000 */
  // ENABLING FLAGS
  flags |= TWO_FACTOR_AUTH_FLAG;            /* 0b00000010 */
  flags |= JUST_A_FLAG_FLAG;                /* 0b00000110 */ 


  printf("flags = %d\n", flags);            /* 6 == 0b00000110*/


  // CHECKING FLAG STATES
  if(flags&JUST_A_FLAG_FLAG) {
    printf("JUST_A_FLAG_FLAG is Enabled\n");
  }


  if(flags&SECURITY_CHECK_FLAG) {
    printf("SECURITY_CHECK_FLAG is enabled\n");
  }


  // Disabling Flags
  flags ^= TWO_FACTOR_AUTH_FLAG;            /* 0b00000100 */
  printf("flags = %d\n", flags);           /* 4 == 0b00000100 */


  // will not print because of flag being displabled
  if(flags&TWO_FACTOR_AUTH_FLAG) {
    printf("TWO_FACTOR_AUTH_FLAG is Enabled\n");
  }


  return EXIT_SUCCESS;
}

You must have definitely wondered, all of this just to save 1 byte? well that’s not entirely true. The increase in memory consumption per boolean flag is one byte, but while using the bitwise flags, you only need an additional byte every 8 flags you add to the program. To increase or decrease the number of flags you just need to change the datatype. if you need only less than 8 flags, you can use “unsigned char ”. If you need more than 8 flags, you can use “unsigned long int” or “unsigned long long int” depending on your requirement.

您肯定想知道,所有这些仅节省1个字节吗? 嗯,这并不完全正确。 每个布尔标志的内存消耗增加一个字节,但是当使用按位标志时,添加到程序中的每个8标志仅需要一个额外的字节。 要增加或减少标志的数量,您只需要更改数据类型。 如果只需要少于8个标志,则可以使用“ unsigned char” 。 如果需要8个以上的标志,则可以根据需要使用“ unsigned long int”“ unsigned long long int”

2.检查奇数和偶数 (2. Checking for Odd and Even Numbers)

Another trick you can achieve using bitwise operators is checking if an integer is even or odd. Let’s see how we can do it.

使用按位运算符可以实现的另一个技巧是检查整数是偶数还是奇数。 让我们看看我们如何做到这一点。

#include <stdio.h>
#include <stdlib.h>


int main(){
  int x=3;
  if(!(x&1)){
    printf("x is even");
  } else {
    printf("x is odd!");
  }
}

So the logic behind !(x&1) is that the binary representation of 1 is 0001 (considering only 4 bits for simplicity) and the place value of the least significant bit is 1. All odd numbers can be represented as 2n+1. So the least significant bit is always 1 for odd numbers. So when compare any odd number with 0001 using the bitwise AND, the output bytes will be 0001. so by using the logical NOT operator, we are able to check for even numbers. For odd numbers you just need to remove the logical NOT and just check (x&1).

因此!(x&1)的逻辑是1的二进制表示形式为0001(为简单起见仅考虑4位),而最低有效位的位置值为1。所有奇数都可以表示为2n + 1。 因此,对于奇数,最低有效位始终为1。 因此,当使用按位AND将任何奇数与0001比较时,输出字节将为0001。因此,通过使用逻辑NOT运算符,我们可以检查偶数。 对于奇数,您只需要删除逻辑NOT,然后检查(x&1)。

3.交换变量而不使用第三个变量 (3. Swapping Variables Without Using a Third Variable)

So the swapping of two variables without involving a third variable can be achieved without the bitwise operators, but since you already know bitwise operators, why not?

因此,无需位运算符就可以在不涉及第三个变量的情况下交换两个变量,但是既然您已经知道位运算符,为什么不呢?

#include <stdlib.h>
#include <stdio.h>


int main(){
  int a=3, b=4;
  printf("a: %d; b: %d\n");
  
          /* a == 0011; b == 0100 */
  a ^= b; /* a == 0111; b == 0100 */
  b ^= a; /* a == 0111; b == 0011 */
  a ^= b; /* a == 0100; b == 0011 */
  
  
  printf("a: %d; b: %d\n");
  return EXIT_SUCCESS;
}

4.转换文本大小写(小写和大写) (4. Converting text casing (Lowercase & Uppercase))

#include <stdlib.h>
#include <stdio.h>


int main(){
  char word[8] = "sREedEv";
  char *wordptr = &word[0];


  while(wordptr < &word[7]) {
    printf("UPPERCASE: %c\n", *wordptr & '_'); // converts the char into uppercase regardless of the current casing
    printf("LOWERCASE: %c\n", *wordptr | ' '); // converts the char into lowercase regardless of the current casing
    wordptr++;
  }


  return EXIT_SUCCESS;
}

So in the above example, we are using the bitwise AND with ‘_’ char as the right operand to convert each character in the string to uppercase. And to convert to a lowercase character, we are using the bitwise OR operator with the space ASCII character as the right operand.

因此,在上面的示例中,我们使用按位AND与'_'char作为右操作数将字符串中的每个字符转换为大写。 为了转换为小写字符,我们使用按位或运算符,将空格ASCII字符用作右操作数。

5.检查数字是否为2的幂 (5. Checking if a number is a power of 2)

#include <stdio.h>
#include <stdlib.h>


int main(){
  int a=32;
  if(a > 0 && (a & (a - 1)) == 0){
    printf("%d is a power of 2", a);
  }
  return EXIT_SUCCESS;
}

To understand how the above condition works, we need to understand a property of powers of two. Powers of two when represented in binary have only one set bit i.e., only one bit has the value of 1. Any power of 2, minus 1 would have binary representation where all bits are set except the bit that represents the next number (which is a power of 2).

要了解上述条件的工作原理,我们需要了解2的幂的性质。 用二进制表示的2的幂仅具有一个置1的位,即,只有1的位具有1的值。任何2的幂减去1将具有二进制表示,其中除代表下一个数字的位(即2的幂。

Using the bitwise AND with the number n and n-1, we can conclude that the number in question is a power of two if it returns 0.

使用数字n和n-1的按位与,我们可以得出结论,如果该数字返回0,则该数字为2的幂。

To understand how the above condition works, we need to understand a property of powers of two. Powers of two when represented in binary have only one set bit i.e., only one bit has the value of 1. Any power of 2, minus 1 would have binary representation where all bits are set except the bit that represents the next number (which is a power of 2).

要了解上述条件的工作原理,我们需要了解2的幂的性质。 用二进制表示的2的幂仅具有一个置1的位,即,只有一位的值是1。任何2的幂减1将具有二进制表示,其中除表示下一个数字的位(即2的幂。

Using the bitwise AND with the number n and n-1, we can conclude that the number in question is a power of two if it returns 0.

使用数字n和n-1的按位与,我们可以得出结论,如果该数字返回0,则该数字为2的幂。

These are just some of the many use cases of bitwise operators. The more you understand the properties of binary numbers, datatypes and encoding the better you’ll be able to utilize these operators. That’s all for now. Byte Byte.

这些只是按位运算符的许多用例中的一些。 您对二进制数字,数据类型和编码的属性了解得越多,就越能够利用这些运算符。 目前为止就这样了。 字节字节。

Image for post

翻译自: https://medium.com/tarkalabs/real-world-uses-of-bitwise-operators-c41429df507f

逻辑运算符 位运算符

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值