คุณจะตั้งค่าล้างและสลับบิตได้อย่างไร


2568

คุณจะตั้งล้างและสลับได้อย่างไร


64
อ่านนี้: graphics.stanford.edu/~seander/bithacks.htmlและเมื่อคุณเป็นผู้เชี่ยวชาญให้อ่านสิ่งนี้: realtimecollisiondetection.net/blog/?p=78
ugasoft

9
นอกจากนี้คุณยังอาจจะสนใจในการตรวจสอบออกบิต Twiddler , Bit twiddling Hacksและรวมกันเมจิกอัลกอริทึม

คำตอบ:


3598

การตั้งค่าเล็กน้อย

ใช้ตัวดำเนินการ OR ระดับบิต ( |) เพื่อตั้งค่าบิต

number |= 1UL << n;

ที่จะตั้งค่าnบิต TH numberของ nควรเป็นศูนย์ถ้าคุณต้องการตั้งค่า1บิตและอื่น ๆ ไม่เกินn-1ถ้าคุณต้องการตั้งnบิตที่

ใช้1ULLถ้าnumberกว้างกว่าunsigned long; โปรโมชั่น1UL << nจะไม่เกิดขึ้นจนกว่าจะได้รับการประเมินที่มันไม่ได้กำหนดพฤติกรรมการเปลี่ยนแปลงด้วยความกว้างของมากกว่า1UL << n longเช่นเดียวกันกับตัวอย่างที่เหลือทั้งหมด

กำลังล้างข้อมูลเล็กน้อย

ใช้ตัวดำเนินการระดับบิตและ AND ( &) เพื่อล้างค่าบิต

number &= ~(1UL << n);

ที่จะล้างnบิต TH numberของ คุณต้องสลับสตริงบิตด้วยตัวดำเนินการ NOT ระดับบิต ( ~) จากนั้นและ

สลับไปมาเล็กน้อย

ผู้ประกอบการ XOR ( ^) สามารถใช้เพื่อสลับบิต

number ^= 1UL << n;

ที่จะสลับnบิต TH numberของ

ตรวจสอบเล็กน้อย

คุณไม่ได้ขอสิ่งนี้ แต่ฉันอาจเพิ่มเข้าไปด้วย

ในการตรวจสอบบิตให้เลื่อนตัวเลข n ไปทางขวาจากนั้นใช้ค่าที่เหมาะสมและมัน

bit = (number >> n) & 1U;

ที่จะทำให้ค่าของnบิต TH ของเข้าไปในตัวแปรnumberbit

การเปลี่ยนบิตที่nเป็นx

การตั้งค่าnบิตที่ th ให้เป็นอย่างใดอย่างหนึ่ง1หรือ0สามารถทำได้ด้วยการต่อไปนี้ในการใช้งาน C ++ ที่สมบูรณ์ของ 2:

number ^= (-x ^ number) & (1UL << n);

บิตnจะถูกตั้งค่าถ้าxเป็น1และเคลียร์ถ้าเป็นx 0หากxมีค่าอื่นคุณจะได้รับขยะ x = !!xจะบูลีนเป็น 0 หรือ 1

ในการทำให้สิ่งนี้เป็นอิสระจากพฤติกรรมการลบล้างของ 2 (ซึ่ง-1มีการตั้งค่าบิตทั้งหมดซึ่งแตกต่างจากการใช้ส่วนประกอบหรือเครื่องหมาย 1 / การใช้งานขนาด C ++) ให้ใช้การปฏิเสธแบบไม่มีเครื่องหมาย

number ^= (-(unsigned long)x ^ number) & (1UL << n);

หรือ

unsigned long newbit = !!x;    // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);

เป็นความคิดที่ดีที่จะใช้ประเภทที่ไม่ได้ลงนามสำหรับการจัดการบิตพกพา

หรือ

number = (number & ~(1UL << n)) | (x << n);

(number & ~(1UL << n))จะล้างnบิตบริบูรณ์และ(x << n)จะตั้งค่าnบิต TH xไป

โดยทั่วไปแล้วก็เป็นความคิดที่ดีที่จะไม่คัดลอก / วางรหัสโดยทั่วไปและผู้คนจำนวนมากใช้มาโครตัวประมวลผลก่อน (เช่นคำตอบจากวิกิของชุมชนต่อไป ) หรือการห่อหุ้มบางประเภท


128
ฉันอยากจะทราบว่าในแพลตฟอร์มที่มีการสนับสนุนพื้นเมืองสำหรับชุดบิต / ล้าง (เช่นไมโครคอนโทรลเลอร์ AVR), คอมไพเลอร์มักจะแปล 'myByte | = (1 << x)' เป็นชุดบิตพื้นเมือง / คำแนะนำที่ชัดเจนทุกครั้งที่ x เป็น ค่าคงที่เช่น: (1 << 5) หรือ const ที่ไม่ได้ลงชื่อ x = 5
แอรอน

52
บิต = หมายเลข & (1 << x); จะไม่ใส่ค่าของบิต x ลงในบิตเว้นแต่บิตจะมีประเภท _Bool (<stdbool.h>) มิฉะนั้น bit = !! (หมายเลข & (1 << x)); จะ ..
Chris Young

23
ทำไมคุณไม่เปลี่ยนคนสุดท้ายเป็นbit = (number >> x) & 1
แอรอนแมน

42
1เป็นintตัวอักษรซึ่งลงนาม ดังนั้นการดำเนินการทั้งหมดที่นี่ดำเนินการกับตัวเลขที่ลงนามซึ่งไม่ได้กำหนดไว้อย่างดีตามมาตรฐาน 1Uมาตรฐานไม่ได้รับประกันสมบูรณ์สองหรือการเปลี่ยนแปลงทางคณิตศาสตร์จึงเป็นสิ่งที่ดีกว่าที่จะใช้
Siyuan Ren

50
ฉันชอบnumber = number & ~(1 << n) | (x << n);เปลี่ยน n-th bit เป็น x
jiasli

459

โดยใช้ไลบรารี c ++ std::bitset<N>มาตรฐาน:

หรือเพิ่มboost::dynamic_bitsetรุ่น:

ไม่จำเป็นต้องม้วนตัวเอง:

#include <bitset>
#include <iostream>

int main()
{
    std::bitset<5> x;

    x[1] = 1;
    x[2] = 0;
    // Note x[0-4]  valid

    std::cout << x << std::endl;
}

[Alpha:] > ./a.out
00010

รุ่น Boost ช่วยให้บิตเซ็ตขนาดรันไทม์เปรียบเทียบกับบิตเซ็ตขนาดคอมไพล์เวลาไลบรารีมาตรฐาน


34
+1 ไม่ใช่ std :: bitset ใช้งานได้จาก "C" แต่ในฐานะผู้เขียนแท็กคำถามของเขา / เธอด้วย "C ++", AFAIK คำตอบของคุณดีที่สุดที่นี่ ... std :: vector <bool> เป็นอีกวิธีหนึ่ง ถ้าใครรู้ข้อดีและข้อเสียของมัน
paercebal

23
@andrewdotnich: vector <bool> คือ (ขออภัย) ความเชี่ยวชาญที่เก็บค่าเป็นบิต ดูgotw.ca/publications/mill09.htmสำหรับข้อมูลเพิ่มเติม ...
Niklas

71
อาจไม่มีใครพูดถึงมันเพราะถูกติดแท็กไว้ ในระบบฝังตัวส่วนใหญ่คุณหลีกเลี่ยง STL เหมือนโรคระบาด และเพิ่มการสนับสนุนน่าจะเป็นนกที่หายากมากที่จะเห็นในหมู่คอมไพเลอร์ส่วนใหญ่
Lundin

17
@ มาร์ตินมันเป็นเรื่องจริงมาก นอกเหนือจากนักฆ่าประสิทธิภาพที่เฉพาะเจาะจงเช่น STL และเทมเพลตแล้วระบบฝังตัวจำนวนมากยังหลีกเลี่ยงไลบรารีมาตรฐานทั้งหมดโดยสิ้นเชิงเพราะมันเป็นความเจ็บปวดในการตรวจสอบ กิ่งก้านสาขาส่วนใหญ่ฝังอยู่ในมาตรฐานเช่น MISRA ซึ่งต้องการเครื่องมือวิเคราะห์รหัสแบบคงที่ (ผู้เชี่ยวชาญด้านซอฟต์แวร์ใด ๆ ควรใช้เครื่องมือดังกล่าว btw ไม่ใช่แค่คนฝังตัว) โดยทั่วไปผู้คนมีสิ่งที่ดีกว่าที่จะทำมากกว่ารันการวิเคราะห์แบบสแตติกผ่านไลบรารี่มาตรฐานทั้งหมด - หากซอร์สโค้ดของมันยังมีอยู่ในคอมไพเลอร์เฉพาะ
Lundin

37
@Lundin: ข้อความของคุณกว้างเกินไป (ไม่มีประโยชน์ที่จะโต้แย้ง) ฉันแน่ใจว่าฉันสามารถค้นหาสถานการณ์ที่พวกเขาเป็นจริง นี่ไม่เปลี่ยนจุดเริ่มต้นของฉัน ทั้งสองคลาสนี้ใช้ได้อย่างสมบูรณ์แบบสำหรับใช้ในระบบสมองกลฝังตัว (และฉันรู้ว่าพวกมันถูกใช้) จุดเริ่มต้นของคุณเกี่ยวกับ STL / Boost ที่ไม่ได้ใช้ในระบบฝังตัวก็ผิดเช่นกัน ฉันแน่ใจว่ามีระบบที่ไม่ได้ใช้และแม้แต่ระบบที่ใช้พวกเขาถูกใช้อย่างมีเหตุผล แต่การบอกว่าไม่ได้ใช้นั้นไม่ถูกต้อง (เพราะมีการใช้ระบบ)
Martin York

248

ตัวเลือกอื่นคือใช้ฟิลด์บิต:

struct bits {
    unsigned int a:1;
    unsigned int b:1;
    unsigned int c:1;
};

struct bits mybits;

กำหนดเขตข้อมูล 3 บิต (จริง ๆ แล้วมันเป็นสามบิต 1 felds) การทำงานของบิตกลายเป็นเรื่องง่ายขึ้น (ฮ่าฮ่า):

หากต้องการตั้งค่าหรือล้างบิต:

mybits.b = 1;
mybits.c = 0;

ในการสลับ:

mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1;  /* all work */

กำลังตรวจสอบเล็กน้อย:

if (mybits.c)  //if mybits.c is non zero the next line below will execute

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


68
ฉันพบเสมอโดยใช้ bitfields เป็นความคิดที่ไม่ดี คุณไม่สามารถควบคุมลำดับที่มีการจัดสรรบิต (จากด้านบนหรือด้านล่าง) ซึ่งทำให้ไม่สามารถเรียงลำดับค่าในวิธีที่มีเสถียรภาพ / พกพาได้ยกเว้นบิต ณ เวลา นอกจากนี้ยังเป็นไปไม่ได้ที่จะผสมคณิตศาสตร์บิต DIY กับ bitfields เช่นการสร้างมาสก์ที่ทดสอบหลายบิตในครั้งเดียว แน่นอนคุณสามารถใช้ && และหวังว่าคอมไพเลอร์จะปรับให้เหมาะสมอย่างถูกต้อง ...
.. GitHub STOP HELPING ICE

34
เขตข้อมูลบิตไม่ดีในหลาย ๆ วิธีฉันเกือบจะสามารถเขียนหนังสือเกี่ยวกับมันได้ ในความเป็นจริงฉันเกือบจะต้องทำเช่นนั้นสำหรับโปรแกรมภาคสนามที่ต้องการการปฏิบัติตาม MISRA-C MISRA-C บังคับให้ทุกพฤติกรรมที่กำหนดไว้ในการนำไปใช้นั้นได้รับการบันทึกไว้ดังนั้นฉันจึงเขียนเรียงความเกี่ยวกับทุกสิ่งที่อาจผิดพลาดได้ในฟิลด์บิต คำสั่งบิต, endianess, padding bits, padding bytes, ปัญหาการจัดตำแหน่งอื่น ๆ , การแปลงประเภทโดยนัยและโดยชัดแจ้งไปยังและจากฟิลด์บิต, UB ถ้า int ไม่ได้ใช้เป็นต้น ให้ใช้ตัวดำเนินการระดับบิตแทนสำหรับข้อบกพร่องที่น้อยกว่าและรหัสแบบพกพา เขตข้อมูลบิตซ้ำซ้อนอย่างสมบูรณ์
Lundin

44
เช่นเดียวกับคุณสมบัติทางภาษาส่วนใหญ่สามารถใช้ฟิลด์บิตได้อย่างถูกต้องหรือสามารถใช้งานได้ในทางที่ผิด หากคุณจำเป็นต้องแพ็คค่าเล็ก ๆ หลาย ๆ ค่าลงใน int เดียวเขตข้อมูลบิตอาจมีประโยชน์มาก ในทางกลับกันถ้าคุณเริ่มตั้งสมมติฐานว่าบิตฟิลด์จับคู่กับค่าที่มีอยู่จริงคุณแค่ถามถึงปัญหา
Ferruccio

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

3
@Yasky และ Ferruccio ได้รับคำตอบที่แตกต่างกันสำหรับ sizeof () สำหรับวิธีการนี้ควรแสดงให้เห็นถึงปัญหาเกี่ยวกับความเข้ากันได้ไม่ใช่เฉพาะกับคอมไพเลอร์ แต่กับฮาร์ดแวร์ บางครั้งเราหลอกตัวเองว่าเราได้แก้ไขปัญหาเหล่านี้ด้วยภาษาหรือ runtimes ที่กำหนด แต่จริงๆแล้วมันลงไปที่ 'มันจะทำงานบนเครื่องของฉันหรือไม่' พวกคุณฝังตัวมีความเคารพของฉัน (และความเห็นอกเห็นใจ)
Kelly S. French

181

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

/* a=target variable, b=bit number to act upon 0-n */
#define BIT_SET(a,b) ((a) |= (1ULL<<(b)))
#define BIT_CLEAR(a,b) ((a) &= ~(1ULL<<(b)))
#define BIT_FLIP(a,b) ((a) ^= (1ULL<<(b)))
#define BIT_CHECK(a,b) (!!((a) & (1ULL<<(b))))        // '!!' to make sure this returns 0 or 1

/* x=target variable, y=mask */
#define BITMASK_SET(x,y) ((x) |= (y))
#define BITMASK_CLEAR(x,y) ((x) &= (~(y)))
#define BITMASK_FLIP(x,y) ((x) ^= (y))
#define BITMASK_CHECK_ALL(x,y) (((x) & (y)) == (y))   // warning: evaluates y twice
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))

17
ฉันรู้ว่านี่เป็นโพสต์อายุ 5 ปี แต่ไม่มีการทำซ้ำการโต้เถียงในมาโครเหล่านั้นเลย Dan
Robert Kelly

11
BITMASK_CHECK(x,y) ((x) & (y))จะต้องเป็น((x) & (y)) == (y)อย่างอื่นมันจะส่งคืนผลลัพธ์ที่ไม่ถูกต้องสำหรับหน้ากาก multibit (เช่น5vs. 3) / * Hello to gravediggers ทั้งหมด
:)

7
1ควรเป็น(uintmax_t)1หรือคล้ายกันในกรณีที่ใครก็ตามพยายามใช้มาโครเหล่านี้กับlongประเภทที่ใหญ่กว่า
MM

2
BITMASK_CHECK_ALL(x,y)สามารถนำมาใช้เป็น!~((~(y))|(x))
Handy999

3
@ Handy999 มันง่ายกว่านิดหน่อยที่จะดูว่าทำไมมันถึงได้ผลหลังจากใช้กฎของ De Morgan และจัดเรียงใหม่เพื่อรับ!(~(x) & (y))
Tavian Barnes

114

บางครั้งมันก็คุ้มค่าที่จะใช้enumเพื่อตั้งชื่อบิต:

enum ThingFlags = {
  ThingMask  = 0x0000,
  ThingFlag0 = 1 << 0,
  ThingFlag1 = 1 << 1,
  ThingError = 1 << 8,
}

จากนั้นใช้ชื่อในภายหลัง คือเขียน

thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}

เพื่อตั้งค่าล้างและทดสอบ วิธีนี้คุณซ่อนหมายเลขเวทย์มนตร์จากส่วนที่เหลือของรหัสของคุณ

นอกจากนั้นฉันรับรองโซลูชันของ Jeremy


1
อีกวิธีหนึ่งที่คุณสามารถทำให้การทำงานแทนclearbits() &= ~เหตุใดคุณจึงใช้ enum สำหรับสิ่งนี้ ฉันคิดว่าสิ่งเหล่านั้นมีไว้สำหรับสร้างตัวแปรเฉพาะที่มีค่าตามอำเภอใจที่ซ่อนอยู่ แต่คุณกำลังกำหนดค่าที่แน่นอนให้กับแต่ละคน ดังนั้นอะไรคือผลประโยชน์เทียบกับการนิยามมันเป็นตัวแปร?
endolith

4
@endolith: การใช้enums สำหรับชุดของค่าคงที่ที่เกี่ยวข้องกลับไปอีกนานในการตั้งโปรแกรม c ฉันสงสัยว่าด้วยคอมไพเลอร์สมัยใหม่มีข้อได้เปรียบเพียงอย่างเดียวconst shortหรืออะไรก็ตามที่พวกเขาถูกจัดกลุ่มเข้าด้วยกันอย่างชัดเจน และเมื่อคุณต้องการสิ่งอื่นนอกเหนือจาก bitmasks คุณจะได้หมายเลขอัตโนมัติ ในหลักสูตร c ++ พวกเขายังมีรูปแบบที่แตกต่างกันซึ่งจะช่วยให้คุณตรวจสอบข้อผิดพลาดคงที่เล็กน้อย
dmckee --- ผู้ดูแลอดีตลูกแมว

คุณจะได้ค่าคงที่ enum ที่ไม่ได้กำหนดหากคุณไม่กำหนดค่าคงที่สำหรับแต่ละค่าที่เป็นไปได้ของบิต เป็นสิ่งที่enum ThingFlagsคุ้มค่าสำหรับThingError|ThingFlag1เช่น?
Luis Colorado

6
หากคุณใช้วิธีนี้โปรดทราบว่าค่าคงที่ enum นั้นเป็นประเภทที่เซ็นชื่อintเสมอ สิ่งนี้สามารถทำให้เกิดข้อบกพร่องทุกประเภทเนื่องจากการเลื่อนตำแหน่งเป็นจำนวนเต็มโดยนัยหรือการดำเนินการระดับบิตในประเภทที่เซ็นชื่อ thingstate = ThingFlag1 >> 1ตัวอย่างเช่นจะเรียกใช้พฤติกรรมที่กำหนดโดยการนำไปปฏิบัติ thingstate = (ThingFlag1 >> x) << yสามารถเรียกใช้พฤติกรรมที่ไม่ได้กำหนด และอื่น ๆ เพื่อความปลอดภัยให้ส่งไปยังประเภทที่ไม่ได้ลงชื่อเสมอ
Lundin

1
@Lundin: ตั้งแต่ C ++ 11 คุณสามารถกำหนดประเภทพื้นฐานของการแจงนับเช่น: enum My16Bits: unsigned short { ... };
Aiken Drum

47

จากbitops.hของ snip-c.zip:

/*
**  Bit set, clear, and test operations
**
**  public domain snippet by Bob Stout
*/

typedef enum {ERROR = -1, FALSE, TRUE} LOGICAL;

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

ตกลงเรามาวิเคราะห์สิ่งต่าง ๆ ...

นิพจน์ทั่วไปที่คุณดูเหมือนจะมีปัญหากับสิ่งเหล่านี้คือ "(1L << (posn))" ทั้งหมดนี้ทำคือสร้างหน้ากากที่มีบิตเดียวและจะทำงานกับประเภทจำนวนเต็มใด ๆ อาร์กิวเมนต์ "posn" ระบุตำแหน่งที่คุณต้องการบิต หาก posn == 0 นิพจน์นี้จะประเมินเป็น:

0000 0000 0000 0000 0000 0000 0000 0001 binary.

หาก posn == 8 ระบบจะประเมินเป็น:

0000 0000 0000 0000 0000 0001 0000 0000 binary.

กล่าวอีกนัยหนึ่งมันเพียงสร้างเขตของ 0 กับ 1 ที่ตำแหน่งที่ระบุ ส่วนที่ยุ่งยากเท่านั้นคือในมาโคร BitClr () ที่เราต้องตั้งค่า 0 บิตเดียวในเขตของ 1 สิ่งนี้สามารถทำได้โดยใช้การเติมเต็มของนิพจน์เดียวกับที่แสดงโดยตัวดำเนินการ tilde (~)

เมื่อสร้างมาสก์แล้วจะมีการนำไปใช้กับอาร์กิวเมนต์ตามที่คุณแนะนำโดยใช้ตัวดำเนินการ bitwise และ (&) หรือ (|) และ xor (^) เนื่องจากมาสก์นั้นมีความยาวชนิดมาโครจึงสามารถทำงานได้ดีกับของ char, short's, int's หรือ long's

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

ไม่มั่นใจ? นี่คือรหัสทดสอบบางส่วน - ฉันใช้ Watcom C พร้อมการเพิ่มประสิทธิภาพเต็มรูปแบบและไม่ใช้ _cdecl ดังนั้นการถอดแยกชิ้นส่วนที่ได้จะเป็นไปอย่างสะอาดที่สุด:

---- [TEST.C] ----------------------------------------- -----------------------

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

int bitmanip(int word)
{
      word = BitSet(word, 2);
      word = BitSet(word, 7);
      word = BitClr(word, 3);
      word = BitFlp(word, 9);
      return word;
}

---- [ทดสอบ. เกี่ยวกับ (ถอดชิ้นส่วน)] -------------------------------------- ---------

Module: C:\BINK\tst.c
Group: 'DGROUP' CONST,CONST2,_DATA,_BSS

Segment: _TEXT  BYTE   00000008 bytes  
 0000  0c 84             bitmanip_       or      al,84H    ; set bits 2 and 7
 0002  80 f4 02                          xor     ah,02H    ; flip bit 9 of EAX (bit 1 of AH)
 0005  24 f7                             and     al,0f7H
 0007  c3                                ret     

No disassembly errors

---- [finis] ------------------------------------------- ----------------------


3
2 สิ่งที่เกี่ยวกับเรื่องนี้: (1) ในการอ่านมาโครของคุณบางคนอาจเชื่ออย่างไม่ถูกต้องว่ามาโครตั้ง / ล้าง / พลิกบิตใน ARG จริง ๆ แล้ว แต่ไม่มีการมอบหมาย (2) test.c ของคุณไม่สมบูรณ์ ฉันสงสัยว่าคุณมีปัญหามากขึ้นในกรณีที่คุณพบปัญหา (การออกกำลังกายของผู้อ่าน)
Dan

19
-1 นี่เป็นเพียงความงงงวยแปลก ๆ ไม่เคยคิดค้นภาษา C อีกครั้งโดยการซ่อนไวยากรณ์ภาษาไว้ด้านหลังแมโครมันเป็นวิธีปฏิบัติที่เลวร้ายมาก จากนั้นความแปลกประหลาดบางอย่าง: อันดับแรก 1L ได้รับการลงชื่อหมายถึงการดำเนินการบิตทั้งหมดจะดำเนินการกับประเภทที่เซ็นชื่อ ทุกสิ่งที่ส่งไปยังมาโครเหล่านี้จะกลับมาอีกครั้งเมื่อลงชื่อยาว ไม่ดี. ประการที่สองสิ่งนี้จะทำงานได้อย่างไม่มีประสิทธิภาพมากบน CPU ที่เล็กลงเนื่องจากมันใช้งานได้นานเมื่อการดำเนินการอาจอยู่ในระดับ int ประการที่สามมาโครที่คล้ายฟังก์ชั่นเป็นรากฐานของความชั่วร้ายทั้งหมด: คุณไม่มีความปลอดภัยใด ๆ ทั้งสิ้น นอกจากนี้ความคิดเห็นก่อนหน้าเกี่ยวกับการมอบหมายไม่ถูกต้องมาก
Lundin

2
นี้จะล้มเหลวถ้าเป็น arg จะต้องมีประเภทที่กว้างที่สุดเพื่อให้ (คุณอาจหนีไปได้)long long1L(uintmax_t)11ull
MM

คุณปรับขนาดโค้ดให้เหมาะสมหรือไม่? ใน CPU หลักของ Intel คุณจะได้รับแผงลงทะเบียนบางส่วนเมื่ออ่าน AX หรือ EAX หลังจากฟังก์ชั่นนี้กลับมาเพราะมันเขียนส่วนประกอบ 8 บิตของ EAX (ใช้ได้กับ CPU AMD หรืออื่น ๆ ที่ไม่ได้เปลี่ยนชื่อการลงทะเบียนบางส่วนแยกจากการลงทะเบียนเต็ม Haswell / Skylake ไม่เปลี่ยนชื่อ AL แยกจากกัน แต่พวกเขาจะเปลี่ยนชื่อ AH )
Peter Cordes

37

ใช้ตัวดำเนินการระดับบิต: & |

วิธีตั้งค่าบิตสุดท้ายใน000b:

foo = foo | 001b

วิธีตรวจสอบบิตสุดท้ายfoo:

if ( foo & 001b ) ....

หากต้องการล้างบิตสุดท้ายในfoo:

foo = foo & 110b

ฉันใช้XXXbเพื่อความชัดเจน คุณอาจจะทำงานร่วมกับการเป็นตัวแทน HEX ขึ้นอยู่กับโครงสร้างข้อมูลที่คุณบรรจุบิต


7
ไม่มีสัญกรณ์ไบนารีใน C. ค่าคงที่จำนวนเต็มแบบไบนารีคือส่วนขยายที่ไม่ได้มาตรฐาน
Lundin

ใช้แฮคเกอร์เพื่อสลับเล็กน้อย:foo = foo ^ MY_MASK
ปีเตอร์ L

ใช้ไม่ได้เพื่อสลับหน้ากากเพื่อทำสิ่งที่ชัดเจน:foo = foo & ~MY_MASK
Peter L

32

สำหรับผู้เริ่มต้นฉันต้องการอธิบายเพิ่มเติมอีกเล็กน้อยด้วยตัวอย่าง:

ตัวอย่าง:

value is 0x55;
bitnum : 3rd.

&ผู้ประกอบการจะใช้ในการตรวจสอบบิต:

0101 0101
&
0000 1000
___________
0000 0000 (mean 0: False). It will work fine if the third bit is 1 (then the answer will be True)

สลับหรือพลิก:

0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)

| ผู้ประกอบการ: ตั้งค่าบิต

0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)

26

นี่คือมาโครบิตเลขคณิตที่ฉันโปรดปรานซึ่งใช้ได้กับอาร์เรย์จำนวนเต็มที่ไม่ได้ลงนามใด ๆ จากunsigned charถึงsize_t(ซึ่งเป็นประเภทที่ใหญ่ที่สุดที่ควรมีประสิทธิภาพในการทำงาน):

#define BITOP(a,b,op) \
 ((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))

ในการตั้งค่าเล็กน้อย:

BITOP(array, bit, |=);

วิธีล้างบิต:

BITOP(array, bit, &=~);

ในการสลับ:

BITOP(array, bit, ^=);

วิธีทดสอบ:

if (BITOP(array, bit, &)) ...

เป็นต้น


5
เป็นการดีที่จะอ่าน แต่ควรตระหนักถึงผลข้างเคียงที่อาจเกิดขึ้นได้ การใช้BITOP(array, bit++, |=);การวนซ้ำมักจะไม่ทำในสิ่งที่ผู้โทรต้องการ
foraidt

จริง =) หนึ่งตัวแปรที่คุณอาจต้องการคือการแยกมันออกเป็น 2 แมโคร, 1 สำหรับการระบุองค์ประกอบของอาเรย์และอีกอันสำหรับการเปลี่ยนบิตเข้าที่, Ala BITCELL(a,b) |= BITMASK(a,b);(ทั้งคู่ใช้aเป็นอาร์กิวเมนต์ในการกำหนดขนาด แต่หลังจะไม่ประเมินค่าaตั้งแต่ จะปรากฏเฉพาะในsizeof)
. GitHub หยุดช่วย ICE

1
@R .. คำตอบนี้เก่ามาก แต่ฉันอาจจะชอบฟังก์ชั่นของมาโครในกรณีนี้
PC Luddite

ไมเนอร์: 3 (size_t)หล่อดูเหมือนจะมีเพียงบางส่วนเพื่อประกันคณิตศาสตร์ไม่ได้ลงนาม%กับ จะ(unsigned)มี
chux - Reinstate Monica

คนที่(size_t)(b)/(8*sizeof *(a))ไม่จำเป็นอาจแคบลงbก่อนการแบ่ง เฉพาะปัญหากับอาร์เรย์บิตที่มีขนาดใหญ่มาก ยังเป็นมาโครที่น่าสนใจ
chux - Reinstate Monica

25

เนื่องจากนี่คือแท็ก "ฝังตัว" ฉันจะถือว่าคุณใช้ไมโครคอนโทรลเลอร์ คำแนะนำข้างต้นทั้งหมดนั้นใช้ได้ & ทำงาน (อ่าน - แก้ไข - เขียน, สหภาพ, structs ฯลฯ )

อย่างไรก็ตามในระหว่างการแข่งขันของการแก้จุดบกพร่องตามสโคปฉันประหลาดใจที่พบว่าวิธีการเหล่านี้มีค่าใช้จ่ายมากในรอบ CPU เมื่อเทียบกับการเขียนค่าโดยตรงกับการลงทะเบียน PORTnSET / PORTnCLEAR ของไมโครซึ่งสร้างความแตกต่างจริง ๆ พินของการสับเปลี่ยนความถี่ ISR

สำหรับผู้ที่ไม่คุ้นเคย: ในตัวอย่างของฉันไมโครมีการลงทะเบียน pin-state ทั่วไป PORTn ซึ่งสะท้อนถึงขาออกดังนั้นการทำ PORTn | = BIT_TO_SET ผลในการอ่าน - แก้ไข - เขียนลงทะเบียนที่ อย่างไรก็ตามการลงทะเบียน PORTnSET / PORTnCLEAR ใช้ '1' เพื่อหมายถึง "โปรดทำให้ bit นี้ 1" (SET) หรือ "โปรดทำให้ bit นี้เป็นศูนย์" (CLEAR) และ '0' เพื่อหมายถึง "ปล่อยให้อยู่คนเดียว" ดังนั้นคุณท้ายด้วยสองที่อยู่พอร์ตขึ้นอยู่กับว่าคุณตั้งค่ากำลังหรือล้างบิต (ไม่เสมอสะดวก) แต่มากปฏิกิริยาได้เร็วขึ้นและรหัสประกอบขนาดเล็ก


Micro คือ Coldfire MCF52259 โดยใช้ C ใน Codewarrior การดู disassembler / asm เป็นแบบฝึกหัดที่มีประโยชน์เพราะมันแสดงขั้นตอนทั้งหมดที่ CPU ต้องทำเพื่อให้ได้การดำเนินการขั้นพื้นฐานที่สุด <br> นอกจากนี้เรายังพบคำแนะนำเกี่ยวกับ CPU-hogging อื่น ๆ ในการวนรอบเวลาที่สำคัญ - การ จำกัด ตัวแปรโดยการทำ var% = max_val ทำให้เกิดการวนรอบ CPU ทุกรอบในขณะที่ทำถ้า (var> max_val) var- = max_val ใช้เท่านั้น คำแนะนำสองสามข้อ <br> คำแนะนำที่ดีสำหรับเคล็ดลับเพิ่มเติมอีกเล็กน้อยอยู่ที่นี่: codeproject.com/Articles/6154/…
John U

ที่สำคัญยิ่งกว่านั้นการลงทะเบียน I / O ที่จับคู่หน่วยความจำของตัวช่วยจัดเตรียมกลไกสำหรับการอัพเดตอะตอมมิก อ่าน / แก้ไข / เขียนสามารถไปได้ไม่ดีมากถ้าลำดับถูกขัดจังหวะ
Ben Voigt

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

24

วิธีบิตฟิลด์มีข้อดีอื่น ๆ ในเวทีที่ฝังตัว คุณสามารถกำหนด struct ที่แมปลงบิตโดยตรงในการลงทะเบียนฮาร์ดแวร์เฉพาะ

struct HwRegister {
    unsigned int errorFlag:1;  // one-bit flag field
    unsigned int Mode:3;       // three-bit mode field
    unsigned int StatusCode:4;  // four-bit status code
};

struct HwRegister CR3342_AReg;

คุณต้องระวังลำดับการบรรจุบิต - ฉันคิดว่ามันเป็น MSB ก่อน แต่สิ่งนี้อาจขึ้นอยู่กับการใช้งาน ตรวจสอบว่าฟิลด์ตัวจัดการคอมไพเลอร์ของคุณข้ามขอบเขตของไบต์ได้อย่างไร

จากนั้นคุณสามารถอ่านเขียนทดสอบค่าแต่ละตัวเหมือนก่อนหน้า


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

1
@Lundin - จริง แต่ระบบฝังตัวบิตเล่นซอ (โดยเฉพาะอย่างยิ่งในการลงทะเบียนฮาร์ดแวร์ซึ่งเป็นสิ่งที่คำตอบของฉันเกี่ยวข้องกับ) จะไม่เป็นพกพาประโยชน์อยู่แล้ว
Roddy

1
อาจไม่ได้อยู่ระหว่าง CPU ที่ต่างกันโดยสิ้นเชิง แต่คุณน่าจะต้องการให้พกพาได้ระหว่างคอมไพเลอร์และระหว่างโครงการต่าง ๆ และมีจำนวนมากที่ฝังตัว "เล่นซอ" ที่ไม่เกี่ยวข้องกับฮาร์ดแวร์เลยเช่นการเข้ารหัส / ถอดรหัสโปรโตคอลข้อมูล
Lundin

... และถ้าคุณติดนิสัยการใช้บิตฟิลด์ในการเขียนโปรแกรมแบบฝังตัวคุณจะพบว่าโค้ด X86 ของคุณทำงานได้เร็วขึ้นและผอมลง ไม่ใช่ในการวัดประสิทธิภาพอย่างง่ายที่คุณมีทั้งเครื่องเพื่อบดขยี้เกณฑ์มาตรฐาน แต่ในสภาพแวดล้อมแบบมัลติทาสกิ้งในโลกแห่งความเป็นจริงที่โปรแกรมแข่งขันกันเพื่อหาทรัพยากร Advantage CISC - เป้าหมายการออกแบบดั้งเดิมคือการสร้าง CPU ให้เร็วกว่าบัสและหน่วยความจำช้า

20

ตรวจสอบบิตที่ตำแหน่งโดยพลการในตัวแปรชนิดที่กำหนดเอง:

#define bit_test(x, y)  ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )

ตัวอย่างการใช้งาน:

int main(void)
{
    unsigned char arr[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF };

    for (int ix = 0; ix < 64; ++ix)
        printf("bit %d is %d\n", ix, bit_test(arr, ix));

    return 0;
}

หมายเหตุ: สิ่งนี้ถูกออกแบบมาให้รวดเร็ว (มีความยืดหยุ่น) และไม่แตกกิ่ง มันส่งผลให้รหัสเครื่อง SPARC ที่มีประสิทธิภาพเมื่อรวบรวม Sun Studio 8; ฉันได้ทดสอบด้วย MSVC ++ 2008 ใน amd64 เป็นไปได้ที่จะสร้างมาโครที่คล้ายกันสำหรับการตั้งค่าและการล้างบิต ความแตกต่างที่สำคัญของการแก้ปัญหานี้เมื่อเทียบกับที่อื่น ๆ ที่นี่คือมันทำงานได้ทุกที่ในตัวแปรชนิดใดก็ได้


20

ทั่วไปมากขึ้นสำหรับบิตแมปที่มีขนาดตามอำเภอใจ:

#define BITS 8
#define BIT_SET(  p, n) (p[(n)/BITS] |=  (0x80>>((n)%BITS)))
#define BIT_CLEAR(p, n) (p[(n)/BITS] &= ~(0x80>>((n)%BITS)))
#define BIT_ISSET(p, n) (p[(n)/BITS] &   (0x80>>((n)%BITS)))

2
CHAR_BITถูกกำหนดไว้แล้วโดยlimits.hที่คุณไม่จำเป็นต้องใส่ในของคุณเองBITS(และในความเป็นจริงคุณทำรหัสของคุณแย่ลงโดยการทำเช่นนั้น)
MM

14

โปรแกรมนี้จะเปลี่ยนบิตข้อมูลใด ๆ จาก 0 เป็น 1 หรือ 1 เป็น 0:

{
    unsigned int data = 0x000000F0;
    int bitpos = 4;
    int bitvalue = 1;
    unsigned int bit = data;
    bit = (bit>>bitpos)&0x00000001;
    int invbitvalue = 0x00000001&(~bitvalue);
    printf("%x\n",bit);

    if (bitvalue == 0)
    {
        if (bit == 0)
            printf("%x\n", data);
        else
        {
             data = (data^(invbitvalue<<bitpos));
             printf("%x\n", data);
        }
    }
    else
    {
        if (bit == 1)
            printf("elseif %x\n", data);
        else
        {
            data = (data|(bitvalue<<bitpos));
            printf("else %x\n", data);
        }
    }
}

14

หากคุณกำลังทำบิดนิด ๆ หน่อย ๆ คุณอาจต้องการใช้มาสก์ซึ่งจะทำให้ทุกอย่างรวดเร็วขึ้น ฟังก์ชั่นต่อไปนี้เร็วมากและยังยืดหยุ่นได้ (ช่วยให้บิตสองบิตในแผนที่บิตขนาดใดก็ได้)

const unsigned char TQuickByteMask[8] =
{
   0x01, 0x02, 0x04, 0x08,
   0x10, 0x20, 0x40, 0x80,
};


/** Set bit in any sized bit mask.
 *
 * @return    none
 *
 * @param     bit    - Bit number.
 * @param     bitmap - Pointer to bitmap.
 */
void TSetBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] |= TQuickByteMask[n];        // Set bit.
}


/** Reset bit in any sized mask.
 *
 * @return  None
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
void TResetBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] &= (~TQuickByteMask[n]);    // Reset bit.
}


/** Toggle bit in any sized bit mask.
 *
 * @return   none
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
void TToggleBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] ^= TQuickByteMask[n];        // Toggle bit.
}


/** Checks specified bit.
 *
 * @return  1 if bit set else 0.
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
short TIsBitSet( short bit, const unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;    // Index to byte.
    n = bit % 8;    // Specific bit in byte.

    // Test bit (logigal AND).
    if (bitmap[x] & TQuickByteMask[n])
        return 1;

    return 0;
}


/** Checks specified bit.
 *
 * @return  1 if bit reset else 0.
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
short TIsBitReset( short bit, const unsigned char *bitmap)
{
    return TIsBitSet(bit, bitmap) ^ 1;
}


/** Count number of bits set in a bitmap.
 *
 * @return   Number of bits set.
 *
 * @param    bitmap - Pointer to bitmap.
 * @param    size   - Bitmap size (in bits).
 *
 * @note    Not very efficient in terms of execution speed. If you are doing
 *        some computationally intense stuff you may need a more complex
 *        implementation which would be faster (especially for big bitmaps).
 *        See (http://graphics.stanford.edu/~seander/bithacks.html).
 */
int TCountBits( const unsigned char *bitmap, int size)
{
    int i, count = 0;

    for (i=0; i<size; i++)
        if (TIsBitSet(i, bitmap))
            count++;

    return count;
}

หมายเหตุหากต้องการตั้งค่าบิต 'n' ในจำนวนเต็ม 16 บิตให้ทำดังนี้:

TSetBit( n, &my_int);

ขึ้นอยู่กับคุณเพื่อให้แน่ใจว่าหมายเลขบิตอยู่ในช่วงของบิตแมปที่คุณส่ง โปรดทราบว่าสำหรับโปรเซสเซอร์ endian เล็ก ๆ น้อย ๆ ที่ไบต์, คำ, dwords, qwords ฯลฯ ทำแผนที่ให้ถูกต้องกันในหน่วยความจำ (เหตุผลหลักที่ตัวประมวลผล endian เล็ก ๆ นั้น 'ดีกว่า' ตัวประมวลผล big-endian อาฉันรู้สึกว่าสงครามลุกเป็นไฟมา บน...).


2
อย่าใช้ตารางสำหรับฟังก์ชั่นที่สามารถนำไปใช้กับผู้ให้บริการรายเดียวได้ TQuickByteMask [n] เทียบเท่ากับ (1 << n) นอกจากนี้การทำให้ข้อโต้แย้งของคุณสั้นก็เป็นความคิดที่แย่มาก จริง ๆ แล้ว / และ% จะเป็นการหารไม่ใช่ bithift / bitwise และเนื่องจากการหารที่เซ็นชื่อด้วยพลังของ 2 ไม่สามารถใช้ bitwise คุณควรทำให้ประเภทอาร์กิวเมนต์ที่ไม่ได้ลงนาม int!
. GitHub หยุดช่วยน้ำแข็ง

ประเด็นนี้คืออะไร? มันทำให้โค้ดช้าลงและอ่านยากขึ้นใช่ไหม ฉันไม่เห็นข้อได้เปรียบเดียวกับมัน 1u << n นั้นง่ายต่อการอ่านสำหรับโปรแกรมเมอร์ C และหวังว่าจะสามารถแปลเป็นคำสั่ง CPU ติ๊กนาฬิกาเดี่ยวได้ ส่วนของคุณจะถูกแปลเป็นประมาณ 10 เห็บหรือแม้แต่เลวถึง 100 เห็บขึ้นอยู่กับว่าสถาปัตยกรรมเฉพาะจัดการแผนก สำหรับฟีเจอร์บิตแมปมันจะเหมาะสมกว่าหากมีตารางการค้นหาที่แปลแต่ละดัชนีบิตเป็นดัชนีไบต์เพื่อปรับให้เหมาะสมสำหรับความเร็ว
Lundin

2
สำหรับ big / little endian, big endian จะจับคู่จำนวนเต็มและข้อมูลดิบ (เช่นสตริง) ในลักษณะเดียวกัน: จากซ้ายไปขวา msb ถึง lsb ตลอดบิตแมปทั้งหมด ในขณะที่ endian เล็ก ๆ น้อย ๆ จะแมปจำนวนเต็มจากซ้ายไปขวาเป็น 7-0, 15-8, 23-18, 31-24 แต่ข้อมูลดิบยังคงอยู่จากซ้ายไปขวา msb ถึง lsb ดังนั้น endian เล็ก ๆ น้อย ๆ ที่ดีกว่าสำหรับอัลกอริทึมของคุณนั้นเกินกว่าฉันอย่างสิ้นเชิงดูเหมือนว่าจะตรงกันข้าม
Lundin

2
@R .. ตารางจะมีประโยชน์ถ้า plattform ของคุณไม่สามารถเปลี่ยนได้อย่างมีประสิทธิภาพเช่น microchip mcu เก่า แต่แน่นอนว่าการแบ่งในตัวอย่างนั้นไม่มีประสิทธิภาพอย่างแน่นอน
jeb

12

ใช้สิ่งนี้:

int ToggleNthBit ( unsigned char n, int num )
{
    if(num & (1 << n))
        num &= ~(1 << n);
    else
        num |= (1 << n);

    return num;
}

5
มันใช้การแยกที่ไม่มีประสิทธิภาพ
asdf

3
@asdf งานของคอมไพเลอร์คือการส่งออกไบนารีที่มีประสิทธิภาพมากที่สุดงานของโปรแกรมเมอร์คือการเขียนรหัสที่ชัดเจน
MM

3
นี่เป็นตัวอย่างที่ดีของการทดสอบการตั้งค่าและการล้างบิต อย่างไรก็ตามมันเป็นวิธีที่ดีมากในการสลับบิต
Ben Voigt

10

การขยายbitsetคำตอบ:

#include <iostream>
#include <bitset>
#include <string>

using namespace std;
int main() {
  bitset<8> byte(std::string("10010011");

  // Set Bit
  byte.set(3); // 10010111

  // Clear Bit
  byte.reset(2); // 10010101

  // Toggle Bit
  byte.flip(7); // 00010101

  cout << byte << endl;

  return 0;
}

10

หากคุณต้องการดำเนินการทั้งหมดนี้ด้วยการเขียนโปรแกรม C ในเคอร์เนล Linuxฉันแนะนำให้ใช้ API มาตรฐานของเคอร์เนล Linux

ดูhttps://www.kernel.org/doc/htmldocs/kernel-api/ch02s03.html

set_bit  Atomically set a bit in memory
clear_bit  Clears a bit in memory
change_bit  Toggle a bit in memory
test_and_set_bit  Set a bit and return its old value
test_and_clear_bit  Clear a bit and return its old value
test_and_change_bit  Change a bit and return its old value
test_bit  Determine whether a bit is set

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


9

Visual C 2010 และคอมไพเลอร์อื่น ๆ อาจมีการสนับสนุนโดยตรงสำหรับการดำเนินการบูลีนบิตมีค่าที่เป็นไปได้สองอย่างเช่นบูลีนดังนั้นเราจึงสามารถใช้บูลีนแทนได้แม้ว่าพวกเขาจะใช้พื้นที่มากกว่าบิตเดียว หน่วยความจำในการเป็นตัวแทนนี้ ใช้งานได้แม้sizeof()ผู้ปฏิบัติงานจะทำงานอย่างถูกต้อง

bool    IsGph[256], IsNotGph[256];

//  Initialize boolean array to detect printable characters
for(i=0; i<sizeof(IsGph); i++)  {
    IsGph[i] = isgraph((unsigned char)i);
}

ดังนั้นสำหรับคำถามของคุณIsGph[i] =1หรือIsGph[i] =0ทำให้การตั้งค่าและการล้างค่า bools เป็นเรื่องง่าย

วิธีค้นหาอักขระที่ไม่สามารถพิมพ์ได้:

//  Initialize boolean array to detect UN-printable characters, 
//  then call function to toggle required bits true, while initializing a 2nd
//  boolean array as the complement of the 1st.
for(i=0; i<sizeof(IsGph); i++)  {
    if(IsGph[i])    {
         IsNotGph[i] = 0;
    }   else   {
         IsNotGph[i] = 1;
    }
}

โปรดทราบว่าไม่มีสิ่งใด "พิเศษ" เกี่ยวกับรหัสนี้ มันปฏิบัติกับบิตเช่นจำนวนเต็ม - ซึ่งในทางเทคนิคมันเป็น จำนวนเต็ม 1 บิตที่สามารถเก็บ 2 ค่าและ 2 ค่าเท่านั้น

ฉันเคยใช้วิธีนี้เพื่อค้นหาระเบียนสินเชื่อที่ซ้ำกันโดยที่ loan_number คือคีย์ ISAM โดยใช้หมายเลขเงินกู้ 6 หลักเป็นดัชนีในอาร์เรย์บิต อย่างรวดเร็วอย่างรวดเร็วและหลังจาก 8 เดือนได้พิสูจน์ว่าระบบเมนเฟรมที่เราได้รับข้อมูลนั้นในความเป็นจริงการทำงานผิดพลาด ความเรียบง่ายของบิตเรย์ทำให้มั่นใจในความถูกต้องสูงมาก - เทียบกับวิธีการค้นหา


std :: bitset นั้นถูกนำมาใช้เป็นบิตโดยคอมไพเลอร์ส่วนใหญ่
galinette

@galinette เห็นด้วย ไฟล์ส่วนหัว #include <bitset> เป็นทรัพยากรที่ดีในเรื่องนี้ นอกจากนี้เวกเตอร์คลาสพิเศษ <bool> สำหรับเมื่อคุณต้องการขนาดของเวกเตอร์ที่จะเปลี่ยน C ++ STL, 2nd Edition, Nicolai M. Josuttis ครอบคลุมพวกเขาอย่างละเอียดใน pgs 650 และ 281 ตามลำดับ C ++ 11 เพิ่มความสามารถใหม่ ๆ ให้กับ std :: bitset สิ่งที่ฉันสนใจเป็นพิเศษคือฟังก์ชันแฮชในคอนเทนเนอร์ที่ไม่ได้เรียงลำดับ ขอบคุณสำหรับหัวขึ้น! ฉันจะลบความคิดเห็นที่สมองพิการ มีขยะเพียงพอบนเว็บ ฉันไม่ต้องการเพิ่ม

3
สิ่งนี้ใช้พื้นที่เก็บข้อมูลอย่างน้อยหนึ่งไบต์สำหรับแต่ละboolรายการ อาจเป็น 4 ไบต์สำหรับการตั้งค่า C89 ที่ใช้intในการติดตั้งbool
MM

@MattMcNabb คุณถูกต้อง ใน C ++ ขนาดของประเภท int ที่จำเป็นในการใช้งานบูลีนไม่ได้ถูกระบุไว้โดยมาตรฐาน ฉันรู้ว่าคำตอบนี้มีข้อผิดพลาดมาแล้ว แต่ตัดสินใจทิ้งไว้ที่นี่เพราะผู้คนเห็นว่ามันมีประโยชน์ สำหรับผู้ที่ต้องการใช้ความคิดเห็นของบิต galinette จะมีประโยชน์มากที่สุดเช่นเดียวกับห้องสมุดบิตของฉันที่นี่ ... stackoverflow.com/a/16534995/1899861

2
@RocketRoy: น่าจะเปลี่ยนประโยคที่อ้างว่านี่เป็นตัวอย่างของ "bit actions"
Ben Voigt

6

ใช้หนึ่งในโอเปอเรเตอร์ตามที่กำหนดไว้ที่นี่ที่นี่

ในการตั้งค่าบิตใช้int x = x | 0x?;โดยที่?ตำแหน่งบิตในรูปแบบไบนารี


2
0xเป็นคำนำหน้าสำหรับตัวอักษรในเลขฐานสิบหกไม่ใช่ไบนารี
Ben Voigt

5

นี่คือมาโครบางตัวที่ฉันใช้:

SET_FLAG(Status, Flag)            ((Status) |= (Flag))
CLEAR_FLAG(Status, Flag)          ((Status) &= ~(Flag))
INVALID_FLAGS(ulFlags, ulAllowed) ((ulFlags) & ~(ulAllowed))
TEST_FLAGS(t,ulMask, ulBit)       (((t)&(ulMask)) == (ulBit))
IS_FLAG_SET(t,ulMask)             TEST_FLAGS(t,ulMask,ulMask)
IS_FLAG_CLEAR(t,ulMask)           TEST_FLAGS(t,ulMask,0)

5

ตัวแปรที่ใช้

int value, pos;

ค่า - ข้อมูล
pos - ตำแหน่งของบิตที่เราสนใจตั้งค่าล้างหรือสลับ

ตั้งค่าบิต:

value = value | 1 << pos;

ล้างข้อมูลเล็กน้อย:

value = value & ~(1 << pos); 

สลับบิต:

value = value ^ 1 << pos;

5
int set_nth_bit(int num, int n){    
    return (num | 1 << n);
}

int clear_nth_bit(int num, int n){    
    return (num & ~( 1 << n));
}

int toggle_nth_bit(int num, int n){    
    return num ^ (1 << n);
}

int check_nth_bit(int num, int n){    
    return num & (1 << n);
}

ประเภทการกลับมาของสามารถcheck_nth_bit bool
Xeverous

@ Xeverous ใช่มันขึ้นอยู่กับความตั้งใจของผู้โทร
Sazzad Hissain Khan

5

ให้สมมติว่าบางสิ่งแรกที่
num = 55จำนวนเต็มเพื่อทำการดำเนินการระดับบิต (ตั้งรับล้างสลับ)
n = 40 ตำแหน่งบิตตามการดำเนินการระดับบิต

ทำอย่างไรจึงจะได้บิต

  1. ที่จะได้รับnthบิตของการเปลี่ยนแปลงทางขวา NUM num, nครั้ง จากนั้นดำเนินการ bitwise และ&ด้วย 1
bit = (num >> n) & 1;

มันทำงานอย่างไร?

       0011 0111 (55 in decimal)
    >>         4 (right shift 4 times)
-----------------
       0000 0011
     & 0000 0001 (1 in decimal)
-----------------
    => 0000 0001 (final result)

วิธีการตั้งค่าบิต?

  1. เพื่อกำหนดจำนวนบิตเฉพาะ เลื่อนซ้าย 1 nครั้ง แล้วดำเนินการ OR ระดับบิตการดำเนินการกับ|num
num |= (1 << n);    // Equivalent to; num = (1 << n) | num;

มันทำงานอย่างไร?

       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
       0001 0000
     | 0011 0111 (55 in decimal)
-----------------
    => 0001 0000 (final result)

วิธีการล้างบิต?

  1. เลื่อนซ้าย 1 nครั้งคือ1 << nครั้งคือ
  2. ดำเนินการเสริม bitwise กับผลลัพธ์ข้างต้น เพื่อให้บิตที่ n กลายเป็น unset และบิตที่เหลือจะถูกตั้งค่าเช่น~ (1 << n)ล้างและส่วนที่เหลือของบิตจะกลายเป็นชุดเช่น
  3. สุดท้ายดำเนินการค่าที่เหมาะสมและการดำเนินงานที่มีผลข้างต้นและ& numสามขั้นตอนข้างต้นร่วมกันสามารถเขียนได้num & (~ (1 << n))ดังนี้

ขั้นตอนในการเคลียร์สักหน่อย

num &= (~(1 << n));    // Equivalent to; num = num & (~(1 << n));

มันทำงานอย่างไร?

       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
     ~ 0001 0000
-----------------
       1110 1111
     & 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)

วิธีการสลับเล็กน้อย?

เพื่อสลับบิตเราใช้ bitor XOR ^ตัวดำเนินการตัวดำเนินการ Bitwise XOR ประเมินผลเป็น 1 ถ้าบิตของตัวถูกดำเนินการทั้งสองสอดคล้องกันแตกต่างกันมิฉะนั้นประเมินเป็น 0

ซึ่งหมายถึงการสลับบิตเราจำเป็นต้องดำเนินการ XOR ด้วยบิตที่คุณต้องการสลับและ 1

num ^= (1 << n);    // Equivalent to; num = num ^ (1 << n);

มันทำงานอย่างไร?

  • ถ้าบิตสลับเป็น 0 แล้ว 0 ^ 1 => 1แล้ว
  • ถ้าบิตสลับเป็น 1 แล้ว, 1 ^ 1 => 0.
       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
       0001 0000
     ^ 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)

การอ่านที่แนะนำ - การออกกำลังกายผู้ประกอบการระดับบิต


ขอบคุณสำหรับคำอธิบายโดยละเอียด นี่คือลิงค์สำหรับปัญหาการปฏิบัติสำหรับลิงค์
Chandra Shekhar

4

คุณจะตั้งค่าล้างและสลับบิตได้อย่างไร

เพื่อจัดการกับข้อผิดพลาดการเข้ารหัสทั่วไปเมื่อพยายามสร้างรูปแบบ:
1ไม่กว้างพอเสมอ

ปัญหาจะเกิดอะไรขึ้นเมื่อnumberเป็นชนิดที่กว้างกว่า1?
xอาจดีเกินไปสำหรับการเปลี่ยนแปลงที่1 << xนำไปสู่พฤติกรรมที่ไม่ได้กำหนด (UB) แม้ว่าxจะไม่ดีเกินไป แต่ก็~อาจไม่พลิกบิตบิตที่สำคัญที่สุดได้เพียงพอ

// assume 32 bit int/unsigned
unsigned long long number = foo();

unsigned x = 40; 
number |= (1 << x);  // UB
number ^= (1 << x);  // UB
number &= ~(1 << x); // UB

x = 10;
number &= ~(1 << x); // Wrong mask, not wide enough

ในการทำประกัน 1 ให้กว้างพอ:

รหัสสามารถใช้1ullหรืออวดรู้(uintmax_t)1และทำให้คอมไพเลอร์ปรับให้เหมาะสม

number |= (1ull << x);
number |= ((uintmax_t)1 << x);

หรือร่าย - ซึ่งใช้สำหรับการเข้ารหัส / ตรวจสอบ / แก้ไขปัญหาการบำรุงรักษาทำให้การร่ายถูกต้องและทันสมัย

number |= (type_of_number)1 << x;

หรือค่อย ๆ ส่งเสริมโดยการบังคับให้การดำเนินการทางคณิตศาสตร์ที่เป็นอย่างน้อยที่กว้างเป็นประเภทของ1number

number |= (number*0 + 1) << x;

เช่นเดียวกับกิจวัตรบิตมากที่สุดดีที่สุดที่จะทำงานร่วมกับที่ไม่ได้ลงชื่อประเภทมากกว่าการลงนามคน


ดูคำถามที่น่าสนใจ! ทั้งnumber |= (type_of_number)1 << x;มิได้number |= (number*0 + 1) << x;จัดสรรเพื่อตั้งค่าบิตเครื่องหมายประเภทลงนาม ... number |= (1ull << x);เป็นเรื่องของความเป็นจริงไม่เป็น มีวิธีพกพาที่จะทำตามตำแหน่ง?
chqrlie

1
@chqrlie IMO วิธีที่ดีที่สุดในการหลีกเลี่ยงการตั้งค่าบิตสัญญาณและการเสี่ยง UB หรือ IDB พร้อมกะคือการใช้ประเภทที่ไม่ได้ลงนาม รหัสลงนามกะแบบพกพาที่มีความซับซ้อนสูงเกินกว่าจะยอมรับได้
chux - Reinstate Monica

3

รุ่น templated C ++ 11 (ใส่ในส่วนหัว):

namespace bit {
    template <typename T1, typename T2> inline void set  (T1 &variable, T2 bit) {variable |=  ((T1)1 << bit);}
    template <typename T1, typename T2> inline void clear(T1 &variable, T2 bit) {variable &= ~((T1)1 << bit);}
    template <typename T1, typename T2> inline void flip (T1 &variable, T2 bit) {variable ^=  ((T1)1 << bit);}
    template <typename T1, typename T2> inline bool test (T1 &variable, T2 bit) {return variable & ((T1)1 << bit);}
}

namespace bitmask {
    template <typename T1, typename T2> inline void set  (T1 &variable, T2 bits) {variable |= bits;}
    template <typename T1, typename T2> inline void clear(T1 &variable, T2 bits) {variable &= ~bits;}
    template <typename T1, typename T2> inline void flip (T1 &variable, T2 bits) {variable ^= bits;}
    template <typename T1, typename T2> inline bool test_all(T1 &variable, T2 bits) {return ((variable & bits) == bits);}
    template <typename T1, typename T2> inline bool test_any(T1 &variable, T2 bits) {return variable & bits;}
}

รหัสนี้เสีย (ทำไมคุณมี;คำจำกัดความฟังก์ชั่นของคุณทำไม?)
melpomene

@melpomene รหัสไม่เสียฉันทดสอบ คุณหมายถึงว่ามันจะไม่รวบรวมหรือผลลัพธ์ที่ผิด? เกี่ยวกับส่วนเสริม ';' ฉันจำไม่ได้ว่าสามารถลบออกได้แน่นอน
Joakim L. Christiansen

(variable & bits == bits)?
melpomene

ขอบคุณสำหรับการสังเกตมันควรจะเป็น((variable & bits) == bits)
Joakim L. Christiansen

ใช้std::bitsetใน c ++ 11
pqnet

0

โปรแกรมนี้ใช้วิธีการแก้ปัญหาข้างต้นของ @ Jeremy หากมีคนต้องการเล่นรอบอย่างรวดเร็ว

public class BitwiseOperations {

    public static void main(String args[]) {

        setABit(0, 4); // set the 4th bit, 0000 -> 1000 [8]
        clearABit(16, 5); // clear the 5th bit, 10000 -> 00000 [0]
        toggleABit(8, 4); // toggle the 4th bit, 1000 -> 0000 [0]
        checkABit(8,4); // check the 4th bit 1000 -> true 
    }

    public static void setABit(int input, int n) {
        input = input | ( 1 << n-1);
        System.out.println(input);
    }


    public static void clearABit(int input, int n) {
        input = input & ~(1 << n-1);
        System.out.println(input);
    }

    public static void toggleABit(int input, int n) {
        input = input ^ (1 << n-1);
        System.out.println(input);
    }

    public static void checkABit(int input, int n) {
        boolean isSet = ((input >> n-1) & 1) == 1; 
        System.out.println(isSet);
    }
}


Output :
8
0
0
true

-2

ลองใช้หนึ่งในฟังก์ชั่นเหล่านี้ในภาษา C เพื่อเปลี่ยน n บิต:

char bitfield;

// Start at 0th position

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}

หรือ

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}

หรือ

void chang_n_bit(int n, int value)
{
    if(value)
        bitfield |= 1 << n;
    else
        bitfield &= ~0 ^ (1 << n);
}

char get_n_bit(int n)
{
    return (bitfield & (1 << n)) ? 1 : 0;
}

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