0xff << n ทำอะไรใน Java

Sep 17 2020

จากอินพุตสตรีมฉันอ่าน 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

คำตอบ

4 Sweeper Sep 17 2020 at 18:27

บางอย่างเช่นนี้?

ใช่ยกเว้นว่าตัวถูกดำเนินการได้รับการเลื่อนตำแหน่งตัวเลขและกลายเป็นตัวเลข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 Yunnosch Sep 17 2020 at 18:29

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

เพื่อให้ได้สิ่งที่น่าจะเป็นความหมายเชิงความหมายของค่าสี่ค่าแรกตามโปรโตคอลที่คุณไม่ได้ระบุ

แล้วทำไมจึงใช้ "วงเวียน" แบบนั้นโดยใช้ตัวดำเนินการเหล่านั้น

การใช้& 0xffทำให้แน่ใจว่าใช้เฉพาะ 8 บิตล่างเท่านั้น
สิ่งนี้ซ้ำซ้อนกล่าวคือไม่จำเป็นในกรณีที่คุณสามารถพึ่งพาค่าในอาร์เรย์ที่มาจากไบต์และด้วยเหตุนี้จึงมีเฉพาะค่าถึงบิต 7 โดยไม่คำนึงถึงบิตที่สูงกว่า (มูลค่า 256, 512, ... ) สิ่งนี้สมเหตุสมผลถ้าคุณไม่ไว้วางใจการดึงข้อมูลไบต์เดี่ยวจากสตรีมไปยังอาร์เรย์
อย่างไรก็ตามใน java (ดังที่ harold ชี้ให้เห็นในความคิดเห็น)

& 0xffไม่ซ้ำซ้อนใน Java เนื่องจากไบต์เป็นประเภทที่มีการลงนามดังนั้นการขยายการลงชื่อเมื่อแปลงเป็น int และสำเนาพิเศษเหล่านั้นของเครื่องหมายจะต้องถูกลบออก ถ้าไบต์ไม่ได้ลงนามมันจะบันทึกรหัสจำนวนมากเช่นนี้

การใช้<< Nด้วยNการทวีคูณของ 8 จะย้ายบิตที่ "เก็บเกี่ยว" แปดบิตไปยังตำแหน่งภายในค่าผลลัพธ์ซึ่งมีอิทธิพลที่ถูกต้องต่อค่าผลลัพธ์ หนึ่งในไบต์ (ฉันตั้งใจจะไม่ใช้ "อันดับแรก" ที่นี่) อยู่ในตำแหน่งที่มีค่าต่ำสุดมันไม่ได้ถูกเลื่อนค่าของมันจะถูกใช้ตามที่เป็นอยู่ ต้องการหนึ่งไบต์เป็นไบต์ที่มีมูลค่าสูงกว่าถัดไปกล่าวคือแทนค่าทวีคูณของ 256 (0x100) และอื่น ๆ

การใช้งาน|ประกอบชิ้นส่วนที่อยู่อย่างถูกต้อง

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

Kayaman Sep 17 2020 at 18:28

มันจะบังคับให้ค่าเป็นไบต์ 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)
Ecto Sep 17 2020 at 18:32

นี่คือการดำเนินการไบนารี

เกิดอะไรขึ้นกันแน่?

& 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. ระวังไม่จำเป็นต้องเป็นผลรวมของตัวเลขเหล่านี้