“สวัสดี อามีโก้!”

"ฉันยังต้องการพูดคุยเกี่ยวกับบิตมาสก์และ XOR"

"คุณรู้อยู่แล้วว่าตัวเลขประกอบด้วยบิต และคุณสามารถดำเนินการต่างๆ กับบิตเหล่านี้ได้ บิตมาสก์เป็นตัวแทนของค่าตรรกะที่แตกต่างกันหลายค่า (ค่าจริง/เท็จ) เป็นจำนวนเต็มเดียว ในกรณีนี้ ค่าบูลีนแต่ละค่าจะสอดคล้องกับ บิตเฉพาะ นี่คือวิธีที่จะทำ:"

"การแทนเลขฐานสองของเลขฐานสอง (1, 2, 4, 8, 16, 32, ...) เกี่ยวข้องกับการตั้งค่าหนึ่งบิตเท่านั้น:"

ตัวเลข การเป็นตัวแทนไบนารี
1 0000 0001
2 0000 0010
4 0000 0100
8 0000 1000
16 0001 0000
19 (ไม่ใช่ยกกำลังสอง) 0001 0011
31 (ไม่ใช่ยกกำลังสอง) 0001 1111

"ดังนั้น จำนวนเต็มใดๆ สามารถถือเป็นอาร์เรย์ของบิตหรืออาร์เรย์ของค่าบูลีน"

"นี่คือวิธีที่คุณสามารถเก็บค่าบูลีนต่างๆ ไว้ในตัวเลขเดียว:"

ค่าบูลีน
boolean a = true;
boolean b = false;
boolean c = true;
boolean d = false;
ค่าบรรจุเป็นตัวเลขเดียว:
int result = 0;
 if (a) result += 1; // 1 == 20 — bit 0
 if (b) result += 2; // 2 == 21 — bit 1
 if (c) result += 4; // 4 == 22 — bit 2
 if (d) result += 8; // 8 == 23 — bit 3

"ตอนนี้แต่ละบิตคือ 1 ถ้าตัวแปรบูลีนที่ตรงกันเป็นจริง"

ในกรณีของเรา ตัวแปร a และ c เป็นจริง ผลลัพธ์จึงเท่ากับ 1+4 == 5

0000 0101
0000 dcba

"ฉันคิดว่าฉันรู้ว่าเกิดอะไรขึ้น"

“ถ้าเข้าใจแล้ว ไปกันเถอะ”

"int มี 32 บิต หนึ่งในนั้นใช้สำหรับเครื่องหมายของตัวเลข และอีก 31 บิตสามารถใช้เก็บค่าของตัวแปรบูลีน 31 ตัว"

"Long มี 64 บิต ซึ่งเราสามารถจัดเก็บตัวแปรบูลีนได้ 63 ตัว"

"ใช่."

"ตัวแปรนับสิบอัดแน่นอยู่ในตัวเลขเดียว นั่นก็ไม่กี่ตัว"

"แต่สิ่งนี้นำไปใช้ที่ไหน"

"โดยหลักแล้วในสถานการณ์ที่คุณต้องการจัดเก็บข้อมูลจำนวนมากเกี่ยวกับวัตถุ เมื่อคุณจัดเก็บข้อมูลจำนวนมากเกี่ยวกับวัตถุหนึ่งๆ จะมีตัวแปรบูลีนสองสามตัวอยู่เสมอ "ด้วยวิธีนี้ พวกมันทั้งหมดจะถูกจัดเก็บไว้ในหมายเลขเดียวได้อย่างสะดวก "

"เน้นคำว่าเก็บไว้ เพราะจริงๆ แล้วการใช้เบอร์ไม่ค่อยสะดวกเท่าไหร่"

"อ้อ นั่นแหละคือสิ่งที่ฉันอยากถาม เราจะแยกค่าบูลีนออกจากตัวเลขได้อย่างไร"

"มันไม่ซับซ้อนเลย สมมติว่าคุณต้องกำหนดว่าบิต 6 ถูกตั้งค่าเป็น 1 หรือไม่ (2 ยกกำลัง 5 คือ 32) เราสามารถตรวจสอบได้ดังนี้:"

รวมตัวเลขเป็นหนึ่ง:
int a = 32; // 25 == 0010 0000
int b = 8; // 23 == 0000 1000
int c = 2; // 21 == 0000 0010

int result = a + b + c; // 32 + 8 + 2 == 42 == 0010 1010
แยกค่าโดยการตรวจสอบบิตเฉพาะ:
int a = result & 32; // 0010 1010 & 0010 0000 = 0010 0000
int b = result & 8; // 0010 1010 & 0000 1000 = 0000 1000
int c = result & 2; // 0010 1010 & 0000 0010 = 0000 0010

"ดังนั้น การทำงานกับบิตมาสก์จึงเกี่ยวข้องกับการดำเนินการสามอย่าง:"

1)  ตั้งค่าบิตเฉพาะเป็น 0

2)  ตั้งค่าบิตเฉพาะเป็น 1

3)  ตรวจสอบค่าของบิตเฉพาะ

"ยกตัวอย่างบิต 6"

"คุณตั้งค่าบิต 6 เป็น 1 ได้อย่างไร"

รหัส คำอธิบาย
result = result | 01000000;
result |= 01000000;
คุณตั้งค่าบิต 6 เป็น 1 ได้อย่างไร
result = result & 10111111;
result &= 10111111;
คุณตั้งค่าบิต 6 เป็น 0 ได้อย่างไร
c = result & 01000000;
คุณจะได้รับค่าของบิต 6 ได้อย่างไร

"นั่นเป็นเรื่องที่ผิดปกติอย่างมาก แต่ไม่ใช่เรื่องยาก ตอนนี้ฉันเป็นโปรแกรมเมอร์ที่ร้อนแรง"

"และอีกหนึ่งเคล็ดลับเล็ก ๆ น้อย ๆ เกี่ยวกับวิธีรับตัวเลขอย่างง่ายดายด้วยบิตเฉพาะที่ตั้งค่าเป็น 0 หรือ 1: 01000000 หรือ 10111111"

สำหรับสิ่งนี้ เรามี  ตัวดำเนินการ>> และ  <<

"1 คือ 2 ยกกำลังศูนย์ กล่าวคือ ตัวเลขที่มีบิต 0 ตั้งค่าเป็น 1 เราต้องการตัวเลขที่มีบิต 6 เซ็ต"

int c = 1 << 6; // 0000 0001 << 6 == 0100 0000 == 64

"เยี่ยมมาก! มีประโยชน์มากสำหรับกรณีเช่นนี้"

"แต่ถ้าฉันต้องการตัวเลขที่ทุกบิตตั้งค่าเป็น 1 ยกเว้นบิตหนึ่งที่กำหนดเป็น 0"

"นั่นก็ไม่ใช่เรื่องยากเช่นกัน:"

int d = ~(1 << 6); // ~0100 0000 == 10111111

"อีกนัยหนึ่ง ทุกอย่างง่ายมาก:"

รหัส คำอธิบาย
result = result | (1 << 6);
result |= (1 << 6);
คุณตั้งค่าบิต 6 เป็น 1 ได้อย่างไร
result = result & ~(1 << 6);
result &= ~(1 << 6);
คุณตั้งค่าบิต 6 เป็น 0 ได้อย่างไร
c = result & (1 << 6);
คุณจะได้รับค่าของบิต 6 ได้อย่างไร

“มันดูไม่ยากนัก แต่ฉันจะไม่จำมันในทันที”

"แต่ถ้าคุณพบนิพจน์ที่น่ากลัว เช่น "result &= ~(1 << 6)" ในโค้ดของคนอื่น คุณจะรู้ว่าคนนี้กำลังทำงานกับ bitmask"

"และถ้าคุณเจอมันบ่อยๆ มันจะจำมันได้เอง"

"จำไว้... ฟังดูดี ขอบคุณสำหรับบทเรียน"