เหตุใดบูลีน 1 ไบต์และไม่ใช่ขนาด 1 บิต


127

ใน C ++

  • เหตุใดบูลีน 1 ไบต์และไม่ใช่ขนาด 1 บิต
  • ทำไมไม่มีประเภทเช่นจำนวนเต็ม 4 บิตหรือ 2 บิต

ฉันพลาดสิ่งต่างๆข้างต้นเมื่อเขียนโปรแกรมจำลองสำหรับ CPU


10
ใน C ++ คุณสามารถ "แพ็ค" ข้อมูลโดยใช้บิตฟิลด์ struct Packed { unsigned int flag1 : 1; unsigned int flag2: 1; };. คอมไพเลอร์ส่วนใหญ่จะจัดสรรแบบเต็มunsigned intอย่างไรก็ตามพวกเขาจัดการกับบิตบิดด้วยตัวเองเมื่อคุณอ่าน / เขียน นอกจากนี้พวกเขาจัดการด้วยตัวเองด้วยการทำงานของโมดูโล นั่นคือunsigned small : 4แอตทริบิวต์ที่มีค่าระหว่าง 0 ถึง 15 และเมื่อควรถึง 16 จะไม่เขียนทับบิตก่อนหน้า :)
Matthieu M.

คำตอบ:


208

เนื่องจาก CPU ไม่สามารถระบุสิ่งที่เล็กกว่าไบต์ได้


10
ประณามตอนนี้ที่น่าอึดอัดใจเซอร์
Asm

31
ที่จริงสี่ x86 คำแนะนำbt, bts, btrและbtc สามารถอยู่บิตเดียว!
fredoverflow

11
ผมคิดว่าbtอยู่ไบต์ชดเชยแล้วทดสอบบิตที่ได้รับชดเชยอักษรโดยไม่คำนึงถึงเมื่อระบุที่อยู่ที่คุณจะไปในไบต์ ... บิตชดเชยจะได้รับคำบิต (แก้ตัวเล่นสำนวน)
user7116

2
@six: คุณสามารถโหลดจุดเริ่มต้นของอาร์เรย์ในรีจิสเตอร์เดียวจากนั้น "บิตออฟเซ็ต" แบบสัมพัทธ์เป็นวินาที บิตออฟเซ็ตไม่ จำกัด เฉพาะ "ภายในหนึ่งไบต์" สามารถเป็นตัวเลข 32 บิตก็ได้
fredoverflow

4
ใช่และไม่ใช่ เรามี bitfields และเราสามารถมีตัวชี้ bitfield นั่นคือ address + bit number เห็นได้ชัดว่าตัวชี้ดังกล่าวจะไม่สามารถแปลงเป็นโมฆะได้ * เนื่องจากข้อกำหนดการจัดเก็บเพิ่มเติมสำหรับหมายเลขบิต
Maxim Egorushkin

32

จากWikipedia :

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

ดังนั้นไบต์เป็นหน่วยแอดเดรสพื้นฐานด้านล่างซึ่งสถาปัตยกรรมคอมพิวเตอร์ไม่สามารถอยู่ และเนื่องจากไม่มี (อาจ) มีคอมพิวเตอร์ที่รองรับไบต์ 4 บิตคุณจึงไม่มี4 บิตเป็นต้น bool

อย่างไรก็ตามหากคุณสามารถออกแบบสถาปัตยกรรมดังกล่าวที่สามารถจัดการ 4 บิตเป็นหน่วยแอดเดรสพื้นฐานได้คุณจะมีboolขนาด 4 บิตในคอมพิวเตอร์เครื่องนั้นเท่านั้น!


4
"คุณจะมี int ขนาด 4 บิตบนคอมพิวเตอร์เครื่องนั้นเท่านั้น" - ไม่คุณจะไม่ทำเพราะมาตรฐานห้ามไม่ให้ CHAR_BIT น้อยกว่า 8 หากหน่วยแอดเดรสบนสถาปัตยกรรมมีขนาดน้อยกว่า 8 บิตแสดงว่า การใช้งาน C ++ จะต้องนำเสนอโมเดลหน่วยความจำที่แตกต่างจากโมเดลหน่วยความจำของฮาร์ดแวร์
Steve Jessop

@ สตีฟ: โอ๊ะ ... ฉันมองข้ามไป ลบintและออกcharจากโพสต์ของฉัน
Nawaz

1
คุณไม่สามารถมี 4 บิตboolได้เนื่องจากcharเป็นหน่วยแอดเดรสที่เล็กที่สุดใน C ++ไม่ว่าสถาปัตยกรรมใดจะสามารถจัดการกับ opcodes ของตัวเองได้ sizeof(bool)ต้องมีค่าอย่างน้อย 1 และboolอ็อบเจ็กต์ที่อยู่ติดกันต้องมีแอดเดรสของตัวเองใน C ++ดังนั้นการนำไปใช้งานเพียงแค่ทำให้มันใหญ่ขึ้นและสิ้นเปลืองหน่วยความจำ นั่นเป็นเหตุผลว่าทำไมเขตข้อมูลบิตจึงมีอยู่เป็นกรณีพิเศษ: สมาชิกบิตฟิลด์ของโครงสร้างไม่จำเป็นต้องระบุแอดเดรสแยกกันดังนั้นจึงมีขนาดเล็กกว่า a ได้char(แม้ว่าโครงสร้างทั้งหมดจะยังไม่สามารถเป็นได้)
Steve Jessop

@ Steve Jessop: นั่นดูน่าสนใจ คุณช่วยกรุณาอ้างอิงจากข้อกำหนดภาษาที่ระบุว่าcharเป็นหน่วยแอดเดรสที่เล็กที่สุดใน C ++ ได้ไหม
Nawaz

3
คำสั่งเฉพาะที่ใกล้เคียงที่สุดน่าจะเป็น 3.9 / 4: "การแสดงอ็อบเจ็กต์ของอ็อบเจ็กต์ประเภท T คือลำดับของอ็อบเจ็กต์ถ่านที่ไม่ได้ลงชื่อ N ที่นำขึ้นโดยอ็อบเจ็กต์ประเภท T โดยที่ N เท่ากับ sizeof (T)" เห็นได้ชัดว่าsizeof(bool)ต้องไม่เป็น 0.5 :-) ฉันคิดว่าการใช้งานสามารถให้ตัวชี้ไบต์ย่อยเป็นส่วนขยายได้ตามกฎหมาย แต่วัตถุ "ธรรมดา" เช่นบูลที่จัดสรรด้วยวิธีธรรมดาต้องทำตามที่มาตรฐานกล่าว
Steve Jessop

12

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

อย่างไรก็ตามสามารถใช้การจัดสรรขนาดบิตใน C ++ ได้ มี std :: vector ความเชี่ยวชาญสำหรับเวกเตอร์บิตและโครงสร้างที่รับรายการขนาดบิต


1
ไม่แน่ใจว่าฉันยอมรับว่าการดำเนินการในระดับบิตช้า และ, nots, xors ฯลฯ นั้นรวดเร็วมาก โดยทั่วไปจะเป็นการดำเนินการตามบิตที่ช้า ในระดับเครื่องค่อนข้างเร็ว แตกแขนง ... ตอนนี้ช้าไปแล้ว
Hogan

3
เพื่อให้ชัดเจนยิ่งขึ้นถ้าคุณสร้างเวกเตอร์ของบูลีนและใส่ 24 บูลีนเข้าไปมันจะใช้เวลา 3 ไบต์เท่านั้น (3 * 8) หากคุณใส่บูลีนอื่นเข้าไปก็จะใช้เวลาอีกไบต์ แต่ถ้าคุณดันบูลีนอื่นมันจะไม่ใช้ไบต์พิเศษใด ๆ เพราะใช้บิต "ฟรี" ในไบต์สุดท้าย
Pedro Loureiro

ใช่ฉันยังสงสัยว่าการทำงานของ bitewise จะช้า :)
Pedro Loureiro

เวกเตอร์บิตไม่สร้างการจัดสรรขนาดบิต พวกเขาสร้างการจัดสรรขนาดไบต์ ไม่สามารถจัดสรรบิตเดียวได้
John Dibling

1
การอ่านบิตเดียวในเวกเตอร์บิตต้องใช้การดำเนินการสามอย่าง: shift และและอื่น shift อีกครั้ง การเขียนเป็นสอง ในขณะที่แต่ละไบต์สามารถเข้าถึงได้ด้วยไบต์เดียว
sukru

7

ย้อนกลับไปในสมัยก่อนที่ฉันต้องเดินไปโรงเรียนท่ามกลางพายุหิมะที่โหมกระหน่ำขึ้นเขาทั้งสองทางและอาหารกลางวันเป็นสัตว์อะไรก็ได้ที่เราสามารถตามหาในป่าหลังโรงเรียนและฆ่าด้วยมือเปล่าคอมพิวเตอร์มีหน่วยความจำน้อยกว่า ในวันนี้ คอมพิวเตอร์เครื่องแรกที่ฉันใช้มี RAM 6K ไม่ใช่ 6 เมกะไบต์ไม่ใช่ 6 กิกะไบต์ 6 กิโลไบต์ ในสภาพแวดล้อมนั้นมันสมเหตุสมผลมากที่จะแพ็คบูลีนลงใน int ให้มากที่สุดเท่าที่จะทำได้ดังนั้นเราจะใช้การดำเนินการเป็นประจำเพื่อนำมันออกและใส่เข้าไป

วันนี้เมื่อผู้คนล้อเลียนคุณว่ามี RAM เพียง 1 GB และที่เดียวที่คุณสามารถหาฮาร์ดไดรฟ์ที่มีน้อยกว่า 200 GB ได้คือที่ร้านขายของเก่ามันก็ไม่คุ้มกับปัญหาในการแพ็คบิต


ยกเว้นเมื่อจัดการกับ Flags สิ่งต่างๆเช่นการตั้งค่าหลายตัวเลือกในบางสิ่ง ... เช่น 00000001 + 00000100 = 00000101
Armstrongest

@ Atomix: ฉันแทบจะไม่ทำแบบนี้อีกแล้ว ถ้าฉันต้องการสองแฟล็กฉันจะสร้างฟิลด์บูลีนสองฟิลด์ ฉันเคยเขียนโค้ดโดยที่ฉันจะแพ็คแฟล็กแบบนั้นแล้วเขียนว่า "if แฟล็ก & 0x110! = 0 แล้ว" หรือสิ่งที่คล้ายกัน แต่นี่เป็นความลับและทุกวันนี้ฉันมักจะสร้างฟิลด์แยกต่างหากและเขียน "if fooFlag || barFlag " แทน. ฉันจะไม่แยกแยะความเป็นไปได้ของกรณีที่การบรรจุแฟล็กแบบนั้นดีกว่าด้วยเหตุผลบางประการ แต่ไม่จำเป็นต้องบันทึกหน่วยความจำเหมือนที่เคยเป็นอีกต่อไป
Jay

2
จริงๆแล้วมันค่อนข้างคุ้มค่ากับปัญหาของคุณในการแพ็คบิตหากคุณต้องการให้การคำนวณของคุณรวดเร็ว - กับข้อมูลจำนวนมากที่คุณเก็บไว้ในหน่วยความจำ บูลีนการบรรจุไม่ได้มีไว้สำหรับพื้นที่จัดเก็บข้อมูลขนาดเล็กเท่านั้น แต่หมายความว่าคุณสามารถอ่านอาร์เรย์อินพุตบูลีนได้เร็วขึ้น 8 เท่า (ในแง่ของแบนด์วิดท์) เมื่อไม่ได้บรรจุหีบห่อและมักจะมีความสำคัญมาก นอกจากนี้คุณสามารถใช้การดำเนินการบิตเช่น popc (จำนวนประชากร) ซึ่งจะเร่งความเร็วการทำงานของคุณบน CPU
einpoklum

2
บูลีนจำนวนมากอย่างแท้จริงคือสิ่งที่คุณทำงานด้วยทุกวันหากคุณทำ: DBMSes การเรียนรู้ของเครื่องการจำลองทางวิทยาศาสตร์และสิ่งอื่น ๆ อีกมากมาย และ - เพียงแค่ทำงานกับพวกเขาหมายถึงการคัดลอก - จากหน่วยความจำไปยังแคช ล้านบูลคืออะไรคิดเป็นพันล้าน
einpoklum

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

6

คุณสามารถใช้เขตข้อมูลบิตเพื่อรับจำนวนเต็มของขนาดย่อย

struct X
{
    int   val:4;   // 4 bit int.
};

แม้ว่าโดยปกติจะใช้เพื่อแมปโครงสร้างกับรูปแบบบิตที่คาดหวังของฮาร์ดแวร์:

struct SomThing   // 1 byte value (on a system where 8 bits is a byte
{
    int   p1:4;   // 4 bit field
    int   p2:3;   // 3 bit field
    int   p3:1;   // 1 bit
};

6

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

แอพเดียวที่รบกวนการบรรจุบูลหลายตัวลงในไบต์เดียวจากประสบการณ์ของฉันคือ Sql Server


6

เนื่องจากไบต์เป็นหน่วยแอดเดรสที่เล็กที่สุดในภาษา

แต่คุณสามารถทำให้บูลใช้เวลา 1 บิตได้เช่นถ้าคุณมีจำนวนมากเช่น ในโครงสร้างเช่นนี้:

struct A
{
  bool a:1, b:1, c:1, d:1, e:1;
};

2

boolสามารถเป็นหนึ่งไบต์ - ขนาดที่เล็กที่สุดที่สามารถระบุได้ของ CPU หรืออาจใหญ่กว่า ไม่แปลกที่จะต้องมีboolขนาดintเพื่อจุดประสงค์ด้านประสิทธิภาพ หากเพื่อวัตถุประสงค์เฉพาะ (เช่นการจำลองฮาร์ดแวร์) คุณต้องการประเภทที่มี N บิตคุณสามารถค้นหาไลบรารีสำหรับสิ่งนั้นได้ (เช่นไลบรารี GBL มีBitSet<N>คลาส) หากคุณกังวลกับขนาดของbool(คุณอาจมีคอนเทนเนอร์ขนาดใหญ่) คุณสามารถแพ็คบิตด้วยตัวคุณเองหรือใช้std::vector<bool>ที่จะทำเพื่อคุณ (โปรดใช้ความระมัดระวังในภายหลังเนื่องจากไม่เป็นไปตามข้อกำหนดของคอนเทนเนอร์)


2

ลองคิดดูว่าคุณจะนำสิ่งนี้ไปใช้ในระดับอีมูเลเตอร์ของคุณอย่างไร ...

bool a[10] = {false};

bool &rbool = a[3];
bool *pbool = a + 3;

assert(pbool == &rbool);
rbool = true;
assert(*pbool);
*pbool = false;
assert(!rbool);

2

เนื่องจากโดยทั่วไปแล้ว CPU จะจัดสรรหน่วยความจำโดยมี 1 ไบต์เป็นหน่วยพื้นฐานแม้ว่า CPU บางตัวเช่น MIPS จะใช้คำ 4 ไบต์ก็ตาม

อย่างไรก็ตามvectorข้อตกลงboolในรูปแบบพิเศษโดยมีการvector<bool>จัดสรรหนึ่งบิตสำหรับแต่ละบูล


1
ฉันเชื่อว่าแม้ซีพียู MIPS จะให้คุณเข้าถึงแต่ละไบต์ได้แม้ว่าจะมีการลงโทษด้านประสิทธิภาพก็ตาม
Paul Tomblin

@ พอล: ใช่คุณพูดถูก แต่โดยทั่วไปคำเฉพาะlw/ swมีการใช้กันอย่างแพร่หลายมากขึ้น
Ryan Li

ไม่รู้เกี่ยวกับ MIPS แต่สถาปัตยกรรม IA-64 อนุญาตให้เข้าถึงเฉพาะในขอบเขต 64 บิตเท่านั้น
Gene Bushuyev

0

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

ข้อมูลเพิ่มเติม: Wikipedia


0

แม้ว่าขนาดต่ำสุดที่เป็นไปได้คือ 1 ไบต์คุณสามารถมีข้อมูลบูลีน 8 บิตใน 1 ไบต์:

http://en.wikipedia.org/wiki/Bit_array

ตัวอย่างเช่นภาษา Julia มี BitArray และฉันอ่านเกี่ยวกับการใช้งาน C ++

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.