Nội dung chính
1. Các toán tử bitwise
C ++ cung cấp các toán tử thao tác 6 bit, thường được gọi là toán tử bitwise:
Toán tử | ký hiệu | Form | Hoạt động |
dịch trái | << | x << y | tất cả các bit trong x dịch chuyển sang trái y bit |
dịch phải | >> | x >> y | tất cả các bit trong x dịch sang phải y bit |
bitwise NOT | ~ | ~x | tất cả các bit trong x được lật |
bitwise AND | & | x & y | mỗi bit trong x VÀ mỗi bit trong y |
bitwise OR | | | x | y | mỗi bit trong x HOẶC mỗi bit trong y |
bitwise XOR | ^ | x ^ y | mỗi bit trong x XOR mỗi bit trong y |
Trong các ví dụ sau, chúng ta sẽ chủ yếu làm việc với các giá trị nhị phân 4 bit. Điều này là để thuận tiện và giữ cho các ví dụ đơn giản. Trong các chương trình thực tế, số lượng bit được sử dụng dựa trên kích thước của đối tượng (ví dụ: một đối tượng 2 byte sẽ lưu trữ 16 bit).
Để dễ đọc, chúng ta cũng sẽ bỏ qua tiền tố 0b bên ngoài các ví dụ code (ví dụ: thay vì 0b0101, chúng ta sẽ chỉ sử dụng 0101).
Toán tử dịch chuyển sang trái theo chiều dọc bit (<<) và dịch chuyển theo chiều ngược chiều sang phải (>>)
Toán tử dịch chuyển trái bit (<<) dịch chuyển các bit sang trái. Toán hạng bên trái là biểu thức để dịch chuyển các bit và toán hạng bên phải là một số nguyên các bit để dịch chuyển sang trái.
Vì vậy, khi chúng ta nói x << 1, chúng ta đang nói “dịch chuyển các bit trong biến x sang trái 1 chỗ”. Các bit mới được chuyển vào từ phía bên phải nhận giá trị 0.
0011 << 1 là 0110
0011 << 2 là 1100
0011 << 3 là 1000
Lưu ý rằng trong trường hợp thứ ba, chúng ta đã dịch chuyển một chút về cuối số! Các bit bị dịch chuyển khỏi phần cuối của số nhị phân sẽ bị mất vĩnh viễn.
Toán tử dịch chuyển sang phải theo chiều bit (>>) dịch chuyển các bit sang phải.
1100 >> 1 là 0110
1100 >> 2 là 0011
1100 >> 3 là 0001
Lưu ý rằng trong trường hợp thứ ba, chúng ta đã dịch chuyển một chút về phía cuối bên phải của số, vì vậy nó bị mất.
Đây là một ví dụ về thực hiện một số dịch chuyển bit:
#include <bitset>
#include <iostream>
int main()
{
std::bitset<4> x { 0b1100 };
std::cout << x << '\n';
std::cout << (x >> 1) << '\n'; // shift right by 1, yielding 0110
std::cout << (x << 1) << '\n'; // shift left by 1, yielding 1000
return 0;
}
This prints:
1100
0110
1000
Lưu ý rằng kết quả của việc áp dụng các toán tử dịch chuyển bit cho một số nguyên có dấu phụ thuộc vào trình biên dịch trước C ++ 20.
Lưu ý:
Trước C ++ 20, không thay đổi số nguyên có dấu (và thậm chí sau đó, có lẽ vẫn tốt hơn nếu sử dụng không dấu)
Gì!? Không phải toán tử << và toán tử >> được sử dụng cho đầu vào và đầu ra?
Các chương trình ngày nay thường không sử dụng nhiều toán tử dịch chuyển trái và phải theo chiều bit để dịch chuyển các bit. Thay vào đó, bạn có xu hướng thấy toán tử dịch chuyển trái theo chiều bit được sử dụng với std :: cout để xuất văn bản. Hãy xem xét chương trình sau:
#include <bitset>
#include <iostream>
int main()
{
unsigned int x { 0b0100 };
x = x << 1; // use operator<< for left shift
std::cout << std::bitset<4>{ x }; // use operator<< for output
return 0;
}
Kết quả:
1000
Trong chương trình trên, làm thế nào để toán tử << biết để dịch chuyển các bit trong một trường hợp và xuất x trong một trường hợp khác? Câu trả lời là std :: cout đã quá tải (cung cấp định nghĩa thay thế cho) toán tử << thực hiện đầu ra bảng điều khiển chứ không phải dịch chuyển bit.
Khi trình biên dịch thấy rằng toán hạng bên trái của operator << là std :: cout, nó biết rằng nó nên gọi operator << rằng std :: cout đã được nạp chồng để thực hiện đầu ra. Nếu toán hạng bên trái là một kiểu tích phân, thì toán tử << biết nó nên thực hiện hành vi dịch chuyển bit thông thường.
2. Điều tương tự cũng áp dụng cho nhà điều hành >>.
Lưu ý rằng nếu bạn đang sử dụng toán tử << cho cả đầu ra và dịch chuyển sang trái, thì bắt buộc phải có dấu ngoặc đơn:
#include <bitset>
#include <iostream>
int main()
{
std::bitset<4> x{ 0b0110 };
std::cout << x << 1 << '\n'; // print value of x (0110), then 1
std::cout << (x << 1) << '\n'; // print x left shifted by 1 (1100)
return 0;
}
Kết quả:
01101
1100
Dòng đầu tiên in giá trị của x (0110), sau đó là chữ 1. Dòng thứ hai in giá trị của x dịch sang trái 1 (1100).
Chúng ta sẽ nói nhiều hơn về quá tải toán tử trong một phần trong tương lai, bao gồm thảo luận về cách nạp chồng toán tử cho các mục đích riêng của bạn.
3. Bitwise NOT
Toán tử NOT bitwise (~) có lẽ là toán tử dễ hiểu nhất trong tất cả các toán tử bitwise. Nó chỉ cần lật từng bit từ 0 đến 1 hoặc ngược lại. Lưu ý rằng kết quả của một bitwise NOT phụ thuộc vào kích thước kiểu dữ liệu của bạn.
Lật 4 bit:
~ 0100 là 1011
Lật 8 bit:
~ 0000 0100 là 1111 1011
Trong cả trường hợp 4 bit và 8 bit, chúng ta bắt đầu với cùng một số (nhị phân 0100 giống với 0000 0100 theo cách tương tự với số thập phân 7 giống với 07), nhưng chúng ta kết thúc với một kết quả khác.
Chúng ta có thể thấy điều này hoạt động trong chương trình sau:
#include <bitset>
#include <iostream>
int main()
{
std::cout << std::bitset<4>{ ~0b0100u } << ' ' << std::bitset<8>{ ~0b0100u };
return 0;
}
Bản in này:
1011 11111011
4. Bitwise OR
Bitwise OR (|) hoạt động giống như đối số OR logic của nó. Tuy nhiên, thay vì áp dụng OR cho các toán hạng để tạo ra một kết quả duy nhất, OR theo từng bit sẽ áp dụng cho từng bit! Ví dụ, hãy xem xét biểu thức 0b0101 | 0b0110.
Để thực hiện (bất kỳ) phép toán bitwise, dễ nhất là xếp hai toán hạng lên như sau:
0 1 0 1 OR
0 1 1 0
và sau đó áp dụng phép toán cho từng cột bit.
Nếu bạn nhớ, lôgic OR đánh giá thành true (1) nếu toán hạng bên trái, bên phải hoặc cả hai đều đúng (1) và 0 nếu ngược lại. Bitwise OR cho kết quả là 1 nếu bit trái, phải hoặc cả hai là 1 và 0 nếu ngược lại. Do đó, biểu thức đánh giá như thế này:
0 1 0 1 OR
0 1 1 0
——-
0 1 1 1
Kết quả của chúng tôi là 0111 nhị phân.
#include <bitset>
#include <iostream>
int main()
{
std::cout << (std::bitset<4>{ 0b0101 } | std::bitset<4>{ 0b0110 });
return 0;
}
Kết quả
0111
Chúng ta có thể làm điều tương tự với các biểu thức OR ghép, chẳng hạn như 0b0111 | 0b0011 | 0b0001. Nếu bất kỳ bit nào trong cột là 1, kết quả của cột đó là 1.
0 1 1 1 OR
0 0 1 1 OR
0 0 0 1
——–
0 1 1 1
Đây là code cho phần trên:
#include <bitset>
#include <iostream>
int main()
{
std::cout << (std::bitset<4>{ 0b0111 } | std::bitset<4>{ 0b0011 } | std::bitset<4>{ 0b0001 });
return 0;
}
Kết quả
0111
5. Bitwise AND
Bitwise AND (&) hoạt động tương tự như trên. Hợp lý AND đánh giá là true nếu cả toán hạng bên trái và bên phải đều đánh giá là true. Bitwise AND đánh giá là true (1) nếu cả hai bit trong cột là 1. Xét biểu thức 0b0101 & 0b0110. Xếp từng bit lên và áp dụng phép toán AND cho từng cột bit:
0 1 0 1 AND
0 1 1 0
——–
0 1 0 0
#include <bitset>
#include <iostream>
int main()
{
std::cout << (std::bitset<4>{ 0b0101 } & std::bitset<4>{ 0b0110 });
return 0;
}
Kết quả này:
0100
Tương tự, chúng ta có thể làm điều tương tự với các biểu thức AND, chẳng hạn như 0b0001 & 0b0011 & 0b0111. Nếu tất cả các bit trong một cột là 1, kết quả của cột đó là 1.
0 0 0 1 AND
0 0 1 1 AND
0 1 1 1
——–
0 0 0 1
#include <bitset>
#include <iostream>
int main()
{
std::cout << (std::bitset<4>{ 0b0001 } & std::bitset<4>{ 0b0011 } & std::bitset<4>{ 0b0111 });
return 0;
}
6. Toán tử gán bitwise
Tương tự như các toán tử gán số học, C ++ cung cấp các toán tử gán theo bit để tạo điều kiện dễ dàng sửa đổi các biến.
Toán tử | ký hiệu | Form | Hoạt động |
Phép gán dịch trái | <<= | x <<= y | Dịch x sang trái y bit |
Gán dịch phải | >>= | x >>= y | Dịch x sang phải theo y bit |
Gán bitwise | |= | x |= y | Gán x | y đến x |
Gán bitwise | &= | x &= y | Gán x & y cho x |
Phép gán XOR theo bit | ^= | x ^= y | Gán x ^ y cho x |
Ví dụ, thay vì viết x = x >> 1 ;, bạn có thể viết x >> = 1 ;.
/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Group: https://www.facebook.com/groups/cafedev.vn/
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
Pinterest: https://www.pinterest.com/cafedevvn/
YouTube: https://www.youtube.com/channel/UCE7zpY_SlHGEgo67pHxqIoA/
*/
#include <bitset>
#include <iostream>
int main()
{
std::bitset<4> bits { 0b0100 };
bits >>= 1;
std::cout << bits;
return 0;
}
Chương trình này in:
0010
7. Tóm lược
Tóm tắt cách đánh giá các hoạt động bitwise sử dụng phương pháp cột:
Khi đánh giá theo bitwise OR, nếu bất kỳ bit nào trong cột là 1, kết quả cho cột đó là 1.
Khi đánh giá bitwise AND, nếu tất cả các bit trong một cột là 1, kết quả cho cột đó là 1.
Khi đánh giá bitwise XOR, nếu có một số bit lẻ trong một cột, kết quả cho cột đó là 1.
Trong bài học tiếp theo, chúng ta sẽ khám phá cách các toán tử này có thể được sử dụng kết hợp với mặt nạ bit để tạo điều kiện thuận lợi cho thao tác trên bit.
Cài ứng dụng cafedev để dễ dàng cập nhật tin và học lập trình mọi lúc mọi nơi tại đây.
Nguồn và Tài liệu tiếng anh tham khảo:
Tài liệu từ cafedev:
- Full series tự học C++ từ cơ bản tới nâng cao tại đây nha.
- Ebook về C++ tại đây.
- Các series tự học lập trình MIỄN PHÍ khác
- Nơi liên hệ hợp tác hoặc quảng cáo cùng Cafedevn tại đây.
Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:
Chào thân ái và quyết thắng!