Note: This article is excerpted from Chapter 2 "deep understanding of computer systems.
Shift operation:
The C language also provides a set of shift operations to move the bit to the left or right. For a single digit expressed as [xn-1, xn-2 ,..., X0] operand x, C expression x <
There is a corresponding right shift operation x> k, but its behavior is a bit subtle. Generally, the machine supports two forms of right shift: Logical right shift and arithmetic right shift. The logic is shifted right to k zeros on the left side. The result is [0 ,..., 0, xn-1, xn-2 ,..., Xk]. The right shift of arithmetic is to add k values with the highest valid bits on the left end. The result is [xn-1 ,..., Xn-1, xn-1, xn-2 ,..., Xk]. This may seem a bit strange, but we will find it very useful for the operation of signed integer data.
Let's look at an example. The following table shows the results of different shift operations on the 8-bit data of some instances.
Operation |
Value |
Parameter X |
[0110 0011] [1001 0101] |
X <4 |
[00110000] [01010000] |
X> 4 (logical right shift) |
[00000110] [00001001] |
X> 4 (arithmetic shift to the right) |
[00000110] [11111001] |
Italic numbers represent the rightmost (left shift) or leftmost (right shift) filled values. It can be seen that all of the other entries involve filling 0. The only exception is the right shift of arithmetic [10010101. Because the highest bit of the operand is 1, the filled value is 1.
The C language standard does not clearly define which type of right shift should be used. For unsigned data (that is, the integer object declared by the qualified word unsigned), The right shift must be logical. For signed data (the declared integer object by default), The right shift of arithmetic or logic is acceptable. Unfortunately, this means that any assumption of one or another form of right shifting code is potentially portable. However, in fact, almost all compilers/machine combinations use the arithmetic right shift for signed data, and many programmers also assume that machines use this right shift.
On the other hand,Java has a clear definition of how to shift right. Expression x> k shifts the right of x arithmetic to k positions, while x> k logically shifts the right of x.
When moving k bits, k is very large here
What is the result of moving k to a data type consisting of w digits? For example, what is the result of calculating the following expression on a 32-bit machine:
Int lval = 0xFEDCBA98 <32; Int lval = 0xFEDCBA98> 36 Unsigned lval = 0xFEDCBA98u> 40; |
The C language standard is very careful to avoid explaining how to do it in this case. On many machines, when a value of w is moved, the shift command only takes into account the low log2w position of the displacement. Therefore, the actual displacement is obtained by calculating the k mod w. For example, on a 32-bit machine using this rule, the preceding three shift operations are 0, 4, and 8 bits respectively. The result is as follows:
Lval 0xFEDCBA98 Aval 0xFEDCBA9 Uval 0x00FEDCBA |
However, this behavior is not guaranteed for the C program, so the shift quantity should be smaller than the word length. On the other hand, Java requires that the displacement quantity be calculated according to the modulus method we mentioned earlier.
Operator priority problems related to shift operations
Some people often write the expression 1 <2 + 3 <4, which is (1 <2) + (3 <4 ). However, in C, the preceding expression is equivalent to 1 <(2 + 3) <4, because the priority of addition (and subtraction) is higher than that of shift operations. Then, according to the combination rule from left to right, the parentheses should be like this (1 <(2 + 3) <4, so the result is 512, instead of the expected 52. In a C expression, wrong priority is a common program error and is often difficult to check. So when you are not sure, please add brackets!