Các toán tử dịch chuyển bit làm chính xác những gì tên của chúng ngụ ý. Họ thay đổi bit. Đây là một giới thiệu ngắn gọn (hoặc không quá ngắn gọn) cho các toán tử thay đổi khác nhau.
Người vận hành
>>
là toán tử dịch chuyển phải số học (hoặc đã ký).
>>>
là toán tử dịch chuyển phải logic (hoặc không dấu).
<<
là toán tử dịch chuyển trái, và đáp ứng nhu cầu của cả hai dịch chuyển logic và số học.
Tất cả những nhà khai thác có thể được áp dụng cho các giá trị số nguyên ( int
, long
, có thể short
và byte
hay char
). Trong một số ngôn ngữ, áp dụng toán tử shift cho bất kỳ kiểu dữ liệu nào nhỏ hơn int
tự động thay đổi kích thước toán hạng thành một int
.
Lưu ý rằng đó <<<
không phải là một toán tử, bởi vì nó sẽ là dự phòng.
Cũng lưu ý rằng C và C ++ không phân biệt giữa các toán tử dịch chuyển phải . Họ chỉ cung cấp >>
toán tử và hành vi dịch chuyển phải được triển khai được xác định cho các loại đã ký. Phần còn lại của câu trả lời sử dụng các toán tử C # / Java.
(Trong tất cả chủ đạo C và C ++ triển khai bao gồm GCC và Clang / LLVM, >>
trên các loại ký là số học Một số mã giả định này, nhưng nó không phải là một cái gì đó bên nhận bảo lãnh tiêu chuẩn Nó không.. Undefined , mặc dù, tiêu chuẩn đòi hỏi triển khai để xác định nó trở thành một Cách này hay cách khác. Tuy nhiên, các dịch chuyển trái của các số có chữ ký âm là hành vi không xác định (tràn số nguyên đã ký). Vì vậy, trừ khi bạn cần dịch chuyển sang phải số học, thường nên thực hiện dịch chuyển bit của bạn với các loại không dấu.)
Ca trái (<<)
Các số nguyên được lưu trữ, trong bộ nhớ, dưới dạng một chuỗi các bit. Ví dụ: số 6 được lưu dưới dạng 32 bit int
sẽ là:
00000000 00000000 00000000 00000110
Chuyển mẫu bit này sang vị trí bên trái ( 6 << 1
) sẽ dẫn đến số 12:
00000000 00000000 00000000 00001100
Như bạn có thể thấy, các chữ số đã dịch chuyển sang trái theo một vị trí và chữ số cuối cùng bên phải được điền bằng 0. Bạn cũng có thể lưu ý rằng dịch chuyển sang trái tương đương với nhân với lũy thừa của 2. Vì vậy, 6 << 1
tương đương với 6 * 2
, và 6 << 3
tương đương với 6 * 8
. Một trình biên dịch tối ưu hóa tốt sẽ thay thế phép nhân bằng ca khi có thể.
Dịch chuyển không tròn
Xin lưu ý rằng đây không phải là ca tròn. Chuyển giá trị này sang trái theo một vị trí ( 3,758,096,384 << 1
):
11100000 00000000 00000000 00000000
kết quả trong 3.221.225.472:
11000000 00000000 00000000 00000000
Chữ số bị dịch chuyển "khỏi cuối" bị mất. Nó không quấn quanh.
Chuyển đúng logic (>>>)
Một sự thay đổi hợp lý bên phải là sự thay đổi đối với sự dịch chuyển trái. Thay vì di chuyển các bit sang trái, chúng chỉ đơn giản là di chuyển sang phải. Ví dụ: thay đổi số 12:
00000000 00000000 00000000 00001100
ở bên phải bởi một vị trí ( 12 >>> 1
) sẽ lấy lại 6 gốc của chúng tôi:
00000000 00000000 00000000 00000110
Vì vậy, chúng ta thấy rằng dịch chuyển sang phải tương đương với sự phân chia theo quyền hạn của 2.
Mất bit đã biến mất
Tuy nhiên, một sự thay đổi không thể lấy lại các bit "bị mất". Ví dụ: nếu chúng ta thay đổi mẫu này:
00111000 00000000 00000000 00000110
ở bên trái 4 vị trí ( 939,524,102 << 4
), chúng tôi nhận được 2.147.483.744:
10000000 00000000 00000000 01100000
và sau đó dịch chuyển trở lại ( (939,524,102 << 4) >>> 4
), chúng tôi nhận được 134,217,734:
00001000 00000000 00000000 00000110
Chúng tôi không thể lấy lại giá trị ban đầu của mình một khi chúng tôi đã mất bit.
Số học phải dịch chuyển (>>)
Sự dịch chuyển số học đúng giống như sự dịch chuyển hợp lý, ngoại trừ thay vì đệm bằng 0, nó thay đổi với bit đáng kể nhất. Điều này là do bit có ý nghĩa nhất là bit dấu , hoặc bit phân biệt số dương và số âm. Bằng cách đệm với bit có ý nghĩa nhất, sự thay đổi bên phải của số học là bảo toàn dấu hiệu.
Ví dụ: nếu chúng ta giải thích mẫu bit này là số âm:
10000000 00000000 00000000 01100000
chúng ta có số -2,147,483,552. Chuyển vị trí này sang đúng 4 vị trí với dịch chuyển số học (-2,147,483,552 >> 4) sẽ cho chúng ta:
11111000 00000000 00000000 00000110
hoặc số -134,217,722.
Vì vậy, chúng tôi thấy rằng chúng tôi đã bảo toàn dấu hiệu của các số âm của mình bằng cách sử dụng dịch chuyển phải số học, thay vì dịch chuyển đúng logic. Và một lần nữa, chúng ta thấy rằng chúng ta đang thực hiện phân chia theo quyền hạn của 2.