Các Toán Tử Bitwise Trong C

Chào bạn, các nhà mã hóa tương lai! Hôm nay, chúng ta sẽ bắt đầu hành trình thú vị vào thế giới các toán tử bitwise trong C. Đừng lo nếu bạn mới bắt đầu học lập trình; tôi sẽ là người hướng dẫn bạn, giải thích mọi thứ từng bước. Vậy, hãy mặc chiếc mũ học tập của bạn lên và hãy nhảy vào!

C - Bitwise Operators

Các Toán Tử Bitwise Là Gì?

Trước khi bắt đầu, hãy hiểu rõ về các toán tử bitwise. Hãy tưởng tượng bạn có một chùm các công tắc đèn. Các toán tử bitwise như những công cụ đặc biệt cho phép bạn kiểm soát các công tắc này theo cách thú vị - bật tắt chúng, hoặc thậm chí đổi trạng thái của chúng. Trong thế giới máy tính, các "công tắc" này thực chất là các bit (0 và 1) tạo nên dữ liệu của chúng ta.

Bây giờ, hãy khám phá từng toán tử bitwise một.

Toán Tử AND Bitwise (&) Trong C

Toán tử AND bitwise như một người bạn chọn lọc khi chỉ nói "có" khi cả hai đầu vào đều đồng ý. Nó so sánh mỗi bit của hai số và chỉ trả về 1 nếu cả hai bit đều là 1. Nếu không, nó sẽ trả về 0.

Hãy xem một ví dụ:

#include <stdio.h>

int main() {
unsigned int a = 60;  // 60 = 0011 1100 trong nhị phân
unsigned int b = 13;  // 13 = 0000 1101 trong nhị phân

printf("a & b = %d\n", a & b);

return 0;
}

Kết quả:

a & b = 12

Đang xảy ra điều gì ở đây? Hãy phân tích nó:

0011 1100  (60 trong nhị phân)
& 0000 1101  (13 trong nhị phân)
----------
0000 1100  (12 trong thập phân)

Thấy chưa? Nó chỉ giữ lại các 1 nơi cả hai số đều có 1? Đó là hiệu ứng kỳ diệu của AND bitwise!

Toán Tử OR Bitwise (|)

Toán tử OR bitwise như một người bạn giàu tình khi nói "có" nếu cả một trong hai đầu vào đồng ý. Nó trả về 1 nếu ít nhất một trong các bit tương ứng là 1.

Dưới đây là một ví dụ:

#include <stdio.h>

int main() {
unsigned int a = 60;  // 60 = 0011 1100 trong nhị phân
unsigned int b = 13;  // 13 = 0000 1101 trong nhị phân

printf("a | b = %d\n", a | b);

return 0;
}

Kết quả:

a | b = 61

Hãy phân tích nó:

0011 1100  (60 trong nhị phân)
| 0000 1101  (13 trong nhị phân)
----------
0011 1101  (61 trong thập phân)

Thấy chưa? Nó giữ lại 1 nếu một trong số đó có 1? Đó là OR bitwise!

Toán Tử XOR (^)

Toán tử XOR như một người bạn lạc quan khi yêu thích điều gì đó khác biệt. Nó trả về 1 nếu các bit khác nhau, và 0 nếu chúng giống nhau.

Hãy xem nó hoạt động:

#include <stdio.h>

int main() {
unsigned int a = 60;  // 60 = 0011 1100 trong nhị phân
unsigned int b = 13;  // 13 = 0000 1101 trong nhị phân

printf("a ^ b = %d\n", a ^ b);

return 0;
}

Kết quả:

a ^ b = 49

Đây là điều gì đang xảy ra:

0011 1100  (60 trong nhị phân)
^ 0000 1101  (13 trong nhị phân)
----------
0011 0001  (49 trong thập phân)

XOR thường được sử dụng trong mật mã học vì dễ đảo ngược lại. Nếu bạn XOR một số hai lần với cùng một giá trị, bạn sẽ nhận lại số gốc. Cool, phải không?

Toán Tử Dịch Trái (<<)

Toán tử dịch trái như một conveyor belt di chuyển các bit sang trái. Nó dịch mỗi bit sang trái theo số vị trí được chỉ định.

Điều này hoạt động như thế nào:

#include <stdio.h>

int main() {
unsigned int a = 60;  // 60 = 0011 1100 trong nhị phân

printf("a << 2 = %d\n", a << 2);

return 0;
}

Kết quả:

a << 2 = 240

Hãy phân tích nó:

0011 1100  (60 trong nhị phân)
<<2 (dịch trái 2)
----------
1111 0000  (240 trong thập phân)

Thấy chưa? Các bit đã di chuyển sang trái, và các 0 mới được điền vào từ phải? Ngoài ra, dịch trái 1 là tương đương với nhân với 2. Vậy dịch trái 2 là như nhân với 4!

Toán Tử Dịch Phải (>>)

Toán tử dịch phải như người đối diện của dịch trái. Nó di chuyển các bit sang phải.

Hãy xem một ví dụ:

#include <stdio.h>

int main() {
unsigned int a = 60;  // 60 = 0011 1100 trong nhị phân

printf("a >> 2 = %d\n", a >> 2);

return 0;
}

Kết quả:

a >> 2 = 15

Đây là điều gì đang xảy ra:

0011 1100  (60 trong nhị phân)
>>2 (dịch phải 2)
----------
0000 1111  (15 trong thập phân)

Các bit đã di chuyển sang phải, và các 0 mới được điền vào từ trái. Dịch phải 1 tương đương với chia cho 2 (ignorng số dư).

Toán Tử Đối Nghịch (~)

Toán tử đối nghịch như một gương cho các bit. Nó lật mỗi bit từ 0 thành 1 và ngược lại.

Điều này hoạt động như thế nào:

#include <stdio.h>

int main() {
unsigned int a = 60;  // 60 = 0011 1100 trong nhị phân

printf("~a = %u\n", ~a);

return 0;
}

Kết quả:

~a = 4294967235

Đã xảy ra điều gì ở đây? Hãy phân tích nó:

0000 0000 0000 0000 0000 0000 0011 1100  (60 trong nhị phân 32 bit)
~(đối nghịch)
----------------------------------------
1111 1111 1111 1111 1111 1111 1100 0011  (4294967235 trong thập phân)

Mỗi 0 đã trở thành 1, và mỗi 1 đã trở thành 0. Kết quả trông lớn vì chúng ta sử dụng unsigned int, nó phân tích tất cả các 1 đó là một số dương.

Kết Luận

Và thế là, các bạn! Chúng ta đã hành trình qua thế giới các toán tử bitwise trong C. Những công cụ mạnh mẽ này có thể có vẻ khó hiểu đầu tiên, nhưng với thực hành, bạn sẽ tìm thấy chúng rất hữu ích cho các nhiệm vụ như làm việc với phần cứng, tối ưu hóa mã, hoặc thậm chí là một số trick thú vị (nếu các bạn có các buổi tiệc bao gồm toán tử nhị phân, đúng là).

Hãy nhớ, chìa khóa để nắm vững các toán tử này là thực hành. Hãy thử viết các chương trình của riêng bạn sử dụng các toán tử này. Thử nghiệm với các số khác nhau và xem những kết quả bạn sẽ nhận được. Trước khi bạn biết nó, bạn sẽ bắt đầu trộn bit với những người tốt nhất!

Chúc mã hóa vui vẻ, và may các bit luôn ở bên bạn!

Toán Tử Ký Hiệu Mô Tả
AND & Trả về 1 nếu cả hai bit đều là 1, nếu không thì 0
OR | Trả về 1 nếu ít nhất một bit là 1, nếu không thì 0
XOR ^ Trả về 1 nếu các bit khác nhau, 0 nếu giống nhau
Dịch Trái << Dịch các bit sang trái theo số vị trí chỉ định
Dịch Phải >> Dịch các bit sang phải theo số vị trí chỉ định
Đối Nghịch ~ Lật mỗi bit (0 thành 1 và ngược lại)

Credits: Image by storyset