0xff << n ทำอะไรใน Java
จากอินพุตสตรีมฉันอ่าน 4 ไบต์แรกและบรรจุสิ่งเหล่านี้เพื่อรับข้อมูลบางอย่างในกรณีนี้คือขนาด / เลนของสตรีม
สำหรับสิ่งนั้นฉันใช้รหัสติดตาม (ตัวอย่างที่ 1) ที่ฉันคัดลอกจากโครงการอื่น
ตัวอย่างที่ 1: ใช้อาร์เรย์แบบไบต์โดยที่ค่าจะถูกอ่านจาก InputStream ไปยังอาร์เรย์ชื่อin_buf []โดยมีความยาว 4 และค่าใดเป็น {0,0,12,26}
ตัวอย่าง 1
int size = (((in_buf[0] & 0xff) << 24) | ((in_buf[1] & 0xff) << 16) |
((in_buf[2] & 0xff) << 8) | (in_buf[3] & 0xff)); // result its 3098
และเป็นผลให้ฉันกลายเป็นมูลค่าของขนาดดี แต่ ..
ฉันต้องอธิบายว่าเกิดอะไรขึ้นที่นี่และฉันพยายามแยกฟังก์ชันทั้งหมดเพื่อดูว่าเกิดอะไรขึ้นและแก้ไขข้อบกพร่องได้ดีขึ้นและฉันก็กลายเป็นผลลัพธ์ที่ตามมา
int byte1 = ((in_buf[0] & 0xff) << 24); // result 0
int byte2 = ((in_buf[1] & 0xff) << 16); // result 0
int byte3 = ((in_buf[2] & 0xff) << 8); // result 3072
int byte4 = (in_buf[3] & 0xff); // result 26
จากนั้นฉันสรุปได้ว่าจากตัวอย่างที่ 1 ผลลัพธ์ของขนาดคือ sume ของ 0 + 0 + 3072 + 26 แต่จะเกิดอะไรขึ้นอย่างรุนแรง (เฉพาะค่า 12 และ 26) ที่นี่ หรือมีการดำเนินการใด
อะไรทำนองนี้?
0000 0000 0000 1100 //12 << 8
0000 1100 0000 0000 // result after << 8
และทำไมเราต้องใช้มาส์ก& 0xff ?
cuz เมื่อคุณดีบัก int byte3a = (in_buf [3] & 0xff) ผลลัพธ์จะเหมือนกับ int byte3b = in_buf [3]; ดังนั้น 12 , * โดยที่ค่า in_buf [3] = 12; ฉันเพิ่มรูปภาพจากผลการแก้ไขข้อบกพร่องของฉัน
เกิดอะไรขึ้นหรือทำให้สิ่งนี้in_buf [3] & 0xffเป็นอย่างไร
0000 0000 0000 1100 (12)
& 0000 0000 1111 1111 (0xff)
-------------------
0000 0000 1111 0011 ?
ภาพหน้าจอจาก Eclipse
คำตอบ
บางอย่างเช่นนี้?
ใช่ยกเว้นว่าตัวถูกดำเนินการได้รับการเลื่อนตำแหน่งตัวเลขและกลายเป็นตัวเลขintดังนั้นในทางเทคนิคคุณควรแสดง 32 บิต
และทำไมเราต้องใช้มาส์ก
& 0xff?
นี่จึงถือว่าไบต์ลบเป็นบวกintsโดยพื้นฐานแล้วสิ่งที่toUnsignedIntกำลังทำ มันไม่ได้ทำอะไรเลยสำหรับไบต์ที่ไม่ใช่ลบ แต่สำหรับไบต์ลบให้พูดว่า-1:
1111 1111
เมื่อได้รับการแปลงเป็นintเนื่องจากการส่งเสริมตัวเลขระบบจะขยายการลงชื่อเป็น 32 บิต นั่นคือเครื่องหมายจะอยู่ในเชิงลบ โดยทั่วไปหมายความว่าถ้าตัวเลขเป็นลบเราจะเพิ่ม 1s มิฉะนั้นเราจะเติม 0s ดังนั้น-1กลายเป็น:
1111 1111 1111 1111 1111 1111 1111 1111
ตอนนี้ถ้าคุณใช้<< 8มันจะเป็น:
1111 1111 1111 1111 1111 1111 0000 0000
ซึ่งก็คือ-256. ตอนนี้เรามาดูกันว่าจะเกิดอะไรขึ้นถ้าคุณทำ& 0xffก่อนหน้า<< 8นี้ โปรโมชั่นตัวเลขเกิดขึ้นและแปลงไบต์ของคุณเป็น 32 เหมือนก่อนหน้านี้ แต่& 0xffได้รับเพียง 8 บิตที่สำคัญน้อยที่สุด! ดังนั้น int ตอนนี้กลายเป็น:
0000 0000 0000 0000 0000 0000 1111 1111
นี่คือวิธีที่คุณจะได้รับไบต์ดั้งเดิมของคุณโดยเติม 0s แล้ว<< 8สิ่งที่ชัดเจนก็คือ:
0000 0000 0000 0000 1111 1111 0000 0000
โค้ดที่แสดงในตัวอย่าง 1 รับค่าสี่ค่าแรกของอาร์เรย์ (ซึ่งน่าจะมีสิ่งที่คุณอธิบายว่าเป็น "สตรีมอินพุต" หรือค่าของอาร์เรย์) จัดเรียงให้เป็นตัวแปรเดียวโดยสันนิษฐานว่าเป็นจำนวนเต็ม (ไม่ได้ลงนาม) ซึ่งมีความกว้างเพียงพอ
เพื่อให้ได้สิ่งที่น่าจะเป็นความหมายเชิงความหมายของค่าสี่ค่าแรกตามโปรโตคอลที่คุณไม่ได้ระบุ
แล้วทำไมจึงใช้ "วงเวียน" แบบนั้นโดยใช้ตัวดำเนินการเหล่านั้น
การใช้& 0xffทำให้แน่ใจว่าใช้เฉพาะ 8 บิตล่างเท่านั้น
สิ่งนี้ซ้ำซ้อนกล่าวคือไม่จำเป็นในกรณีที่คุณสามารถพึ่งพาค่าในอาร์เรย์ที่มาจากไบต์และด้วยเหตุนี้จึงมีเฉพาะค่าถึงบิต 7 โดยไม่คำนึงถึงบิตที่สูงกว่า (มูลค่า 256, 512, ... ) สิ่งนี้สมเหตุสมผลถ้าคุณไม่ไว้วางใจการดึงข้อมูลไบต์เดี่ยวจากสตรีมไปยังอาร์เรย์
อย่างไรก็ตามใน java (ดังที่ harold ชี้ให้เห็นในความคิดเห็น)
& 0xffไม่ซ้ำซ้อนใน Java เนื่องจากไบต์เป็นประเภทที่มีการลงนามดังนั้นการขยายการลงชื่อเมื่อแปลงเป็น int และสำเนาพิเศษเหล่านั้นของเครื่องหมายจะต้องถูกลบออก ถ้าไบต์ไม่ได้ลงนามมันจะบันทึกรหัสจำนวนมากเช่นนี้
การใช้<< Nด้วยNการทวีคูณของ 8 จะย้ายบิตที่ "เก็บเกี่ยว" แปดบิตไปยังตำแหน่งภายในค่าผลลัพธ์ซึ่งมีอิทธิพลที่ถูกต้องต่อค่าผลลัพธ์ หนึ่งในไบต์ (ฉันตั้งใจจะไม่ใช้ "อันดับแรก" ที่นี่) อยู่ในตำแหน่งที่มีค่าต่ำสุดมันไม่ได้ถูกเลื่อนค่าของมันจะถูกใช้ตามที่เป็นอยู่ ต้องการหนึ่งไบต์เป็นไบต์ที่มีมูลค่าสูงกว่าถัดไปกล่าวคือแทนค่าทวีคูณของ 256 (0x100) และอื่น ๆ
การใช้งาน|ประกอบชิ้นส่วนที่อยู่อย่างถูกต้อง
ทำไมต้องทำอย่างนั้นด้วยวิธีที่ซับซ้อนเช่นนี้? ทางเลือกที่ชัดเจนคือฆ่าบิตทั้งหมดที่เกินบิต 7 แล้วอ่านล็อตเป็นจำนวนเต็ม 4 ไบต์หนึ่งตัว
เหตุผลคือความสงสัยในความอดทน คุณอาจไม่ไว้วางใจว่าการจัดเรียงสี่ไบต์ตามลำดับที่กำหนดในหน่วยความจำหากอ่านเป็นค่า 4 ไบต์หนึ่งค่าจะถูกตีความว่าเป็นค่าที่คุณกำหนดเป้าหมายในสภาพแวดล้อมของคุณ เนื่องจากสภาพแวดล้อมที่แตกต่างกันอาจมีความเห็นที่แตกต่างกันว่าควรพิจารณาไบต์ที่อยู่ต่ำสุดหรือไม่ซึ่งเป็นตัวแทนของค่าต่ำสุดหรือค่าสูงสุด
รหัสที่แสดงจะบังคับให้ไบต์ที่เลือกเป็นค่าสูงค่าหนึ่งเป็นค่าต่ำและค่าอื่น ๆ ที่อยู่ระหว่าง
มันจะบังคับให้ค่าเป็นไบต์ 8 บิต
ตั้งแต่ Java ไม่มีประเภทที่ไม่ได้ลงชื่อค่า byte ของ0xFFถูกตีความว่าเป็น-1และเนื่องจากลักษณะของ Java ก็จะมีการส่งเสริมให้ int 32 0xFFFFFFFFบิตคือ
bitmask |จะหมายถึงการทิ้งบิตพิเศษเหล่านั้นสำหรับค่าลบดังนั้นพวกเขาจึงไม่เขียนทับบิตเมื่อใช้หรือผู้ประกอบการระดับบิต
นี่คือตัวอย่างที่มีค่าไบต์255แม้ว่าจะถูกตีความ-1และเลื่อนขั้นเป็นint
1111 1111 1111 1111 1111 1111 1111 (-1, but was originally an unsigned byte of 255)
& 0000 0000 0000 0000 0000 1111 1111 (0xff)
----------------------------------
0000 0000 0000 0000 0000 1111 1111 (an int with the original value of 255)
นี่คือการดำเนินการไบนารี
เกิดอะไรขึ้นกันแน่?
& 0xFFหมายโดยทั่วไปคุณใช้ไบต์ที่ผ่านมา
เลขฐานสองและ (&) หมายถึงตำแหน่งผลลัพธ์จะเป็น 1 ก็ต่อเมื่อ 1 อยู่ในตัวเลขทั้งสองที่ตำแหน่งเดียวกัน (0 มิฉะนั้น)
มีอีกตัวอย่างหนึ่ง:
0000 0001 0000 1100 (268)
& 0000 0000 1111 1111 (0xff)
-------------------
0000 0000 0000 1100 (12)
เกิดอะไรขึ้นกับ 12?
0000 0000 0000 1100 (12)
& 0000 0000 1111 1111 (0xff)
-------------------
0000 0000 0000 1100 (12)
กะ:
การเลื่อนด้านซ้ายเพียงแค่ดันบิตไปทางซ้าย
0000 0000 0000 1100 (12)
<< 8
-------------------
0000 1100 0000 0000 (3072)
เกิดอะไรขึ้นกับ 26?
0000 0000 0001 1010 (26)
& 0000 0000 1111 1111 (0xff)
-------------------
0000 0000 0001 1010 (26)
ทำไมเราต้องใช้มาสก์ & 0xff?
ในกรณีนี้คุณไม่ทำ แต่ถ้าคุณทำงานกับตัวเลขที่มากขึ้น (หรือจำนวนลบ) อาจจำเป็นต้องใช้มาสก์ นอกจากนี้คุณต้องแน่ใจว่าไม่มีบิตที่ยุ่งเหยิงเป็นพิเศษ
รวมทั้งหมดเข้าด้วยกันหรือ:
ใช่มันเป็นการเพิ่มโดยทั่วไป แต่ในกรณีนี้เท่านั้น สิ่งที่เกิดขึ้นจริงคือคุณใส่ไบต์ที่คุณสร้างไว้& 0xFFติดกันเพื่อสร้างตัวเลข นั่นคือเหตุผลที่คุณขยับด้วยการคูณ 8 - ทุกๆไบต์มีขนาด 8 เป็นของตัวเอง
สมมติว่าคุณมีไบต์ X, Y, Z, W จากนั้น(X<<24)|(Y<<16)|(Z<<8)|Wจะสร้างตัวเลขที่สร้างในไบนารีดังต่อไปนี้: XYZW. ระวังไม่จำเป็นต้องเป็นผลรวมของตัวเลขเหล่านี้