คุณจะตั้งล้างและสลับได้อย่างไร
คุณจะตั้งล้างและสลับได้อย่างไร
คำตอบ:
ใช้ตัวดำเนินการ 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 ของเข้าไปในตัวแปรnumber
bit
การตั้งค่า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
ไป
โดยทั่วไปแล้วก็เป็นความคิดที่ดีที่จะไม่คัดลอก / วางรหัสโดยทั่วไปและผู้คนจำนวนมากใช้มาโครตัวประมวลผลก่อน (เช่นคำตอบจากวิกิของชุมชนต่อไป ) หรือการห่อหุ้มบางประเภท
bit = (number >> x) & 1
1
เป็นint
ตัวอักษรซึ่งลงนาม ดังนั้นการดำเนินการทั้งหมดที่นี่ดำเนินการกับตัวเลขที่ลงนามซึ่งไม่ได้กำหนดไว้อย่างดีตามมาตรฐาน 1U
มาตรฐานไม่ได้รับประกันสมบูรณ์สองหรือการเปลี่ยนแปลงทางคณิตศาสตร์จึงเป็นสิ่งที่ดีกว่าที่จะใช้
number = number & ~(1 << n) | (x << n);
เปลี่ยน n-th bit เป็น x
โดยใช้ไลบรารี 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 ช่วยให้บิตเซ็ตขนาดรันไทม์เปรียบเทียบกับบิตเซ็ตขนาดคอมไพล์เวลาไลบรารีมาตรฐาน
ตัวเลือกอื่นคือใช้ฟิลด์บิต:
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
ใช้ได้กับฟิลด์บิตขนาดคงที่เท่านั้น มิฉะนั้นคุณจะต้องหันไปใช้เทคนิคการทวิบิตที่อธิบายไว้ในโพสต์ก่อนหน้า
ฉันใช้มาโครที่กำหนดไว้ในไฟล์ส่วนหัวเพื่อจัดการชุดบิตและล้าง:
/* 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))
BITMASK_CHECK(x,y) ((x) & (y))
จะต้องเป็น((x) & (y)) == (y)
อย่างอื่นมันจะส่งคืนผลลัพธ์ที่ไม่ถูกต้องสำหรับหน้ากาก multibit (เช่น5
vs. 3
) / * Hello to gravediggers ทั้งหมด
1
ควรเป็น(uintmax_t)1
หรือคล้ายกันในกรณีที่ใครก็ตามพยายามใช้มาโครเหล่านี้กับlong
ประเภทที่ใหญ่กว่า
BITMASK_CHECK_ALL(x,y)
สามารถนำมาใช้เป็น!~((~(y))|(x))
!(~(x) & (y))
บางครั้งมันก็คุ้มค่าที่จะใช้enum
เพื่อตั้งชื่อบิต:
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
จากนั้นใช้ชื่อในภายหลัง คือเขียน
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
เพื่อตั้งค่าล้างและทดสอบ วิธีนี้คุณซ่อนหมายเลขเวทย์มนตร์จากส่วนที่เหลือของรหัสของคุณ
นอกจากนั้นฉันรับรองโซลูชันของ Jeremy
clearbits()
&= ~
เหตุใดคุณจึงใช้ enum สำหรับสิ่งนี้ ฉันคิดว่าสิ่งเหล่านั้นมีไว้สำหรับสร้างตัวแปรเฉพาะที่มีค่าตามอำเภอใจที่ซ่อนอยู่ แต่คุณกำลังกำหนดค่าที่แน่นอนให้กับแต่ละคน ดังนั้นอะไรคือผลประโยชน์เทียบกับการนิยามมันเป็นตัวแปร?
enum
s สำหรับชุดของค่าคงที่ที่เกี่ยวข้องกลับไปอีกนานในการตั้งโปรแกรม c ฉันสงสัยว่าด้วยคอมไพเลอร์สมัยใหม่มีข้อได้เปรียบเพียงอย่างเดียวconst short
หรืออะไรก็ตามที่พวกเขาถูกจัดกลุ่มเข้าด้วยกันอย่างชัดเจน และเมื่อคุณต้องการสิ่งอื่นนอกเหนือจาก bitmasks คุณจะได้หมายเลขอัตโนมัติ ในหลักสูตร c ++ พวกเขายังมีรูปแบบที่แตกต่างกันซึ่งจะช่วยให้คุณตรวจสอบข้อผิดพลาดคงที่เล็กน้อย
enum ThingFlags
คุ้มค่าสำหรับThingError|ThingFlag1
เช่น?
int
เสมอ สิ่งนี้สามารถทำให้เกิดข้อบกพร่องทุกประเภทเนื่องจากการเลื่อนตำแหน่งเป็นจำนวนเต็มโดยนัยหรือการดำเนินการระดับบิตในประเภทที่เซ็นชื่อ thingstate = ThingFlag1 >> 1
ตัวอย่างเช่นจะเรียกใช้พฤติกรรมที่กำหนดโดยการนำไปปฏิบัติ thingstate = (ThingFlag1 >> x) << y
สามารถเรียกใช้พฤติกรรมที่ไม่ได้กำหนด และอื่น ๆ เพื่อความปลอดภัยให้ส่งไปยังประเภทที่ไม่ได้ลงชื่อเสมอ
enum My16Bits: unsigned short { ... };
/*
** 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] ------------------------------------------- ----------------------
arg
จะต้องมีประเภทที่กว้างที่สุดเพื่อให้ (คุณอาจหนีไปได้)long long
1L
(uintmax_t)1
1ull
ใช้ตัวดำเนินการระดับบิต: &
|
วิธีตั้งค่าบิตสุดท้ายใน000b
:
foo = foo | 001b
วิธีตรวจสอบบิตสุดท้ายfoo
:
if ( foo & 001b ) ....
หากต้องการล้างบิตสุดท้ายในfoo
:
foo = foo & 110b
ฉันใช้XXXb
เพื่อความชัดเจน คุณอาจจะทำงานร่วมกับการเป็นตัวแทน HEX ขึ้นอยู่กับโครงสร้างข้อมูลที่คุณบรรจุบิต
foo = foo ^ MY_MASK
foo = foo & ~MY_MASK
สำหรับผู้เริ่มต้นฉันต้องการอธิบายเพิ่มเติมอีกเล็กน้อยด้วยตัวอย่าง:
ตัวอย่าง:
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)
นี่คือมาโครบิตเลขคณิตที่ฉันโปรดปรานซึ่งใช้ได้กับอาร์เรย์จำนวนเต็มที่ไม่ได้ลงนามใด ๆ จาก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, &)) ...
เป็นต้น
BITOP(array, bit++, |=);
การวนซ้ำมักจะไม่ทำในสิ่งที่ผู้โทรต้องการ
BITCELL(a,b) |= BITMASK(a,b);
(ทั้งคู่ใช้a
เป็นอาร์กิวเมนต์ในการกำหนดขนาด แต่หลังจะไม่ประเมินค่าa
ตั้งแต่ จะปรากฏเฉพาะในsizeof
)
(size_t)
หล่อดูเหมือนจะมีเพียงบางส่วนเพื่อประกันคณิตศาสตร์ไม่ได้ลงนาม%
กับ จะ(unsigned)
มี
(size_t)(b)/(8*sizeof *(a))
ไม่จำเป็นอาจแคบลงb
ก่อนการแบ่ง เฉพาะปัญหากับอาร์เรย์บิตที่มีขนาดใหญ่มาก ยังเป็นมาโครที่น่าสนใจ
เนื่องจากนี่คือแท็ก "ฝังตัว" ฉันจะถือว่าคุณใช้ไมโครคอนโทรลเลอร์ คำแนะนำข้างต้นทั้งหมดนั้นใช้ได้ & ทำงาน (อ่าน - แก้ไข - เขียน, สหภาพ, structs ฯลฯ )
อย่างไรก็ตามในระหว่างการแข่งขันของการแก้จุดบกพร่องตามสโคปฉันประหลาดใจที่พบว่าวิธีการเหล่านี้มีค่าใช้จ่ายมากในรอบ CPU เมื่อเทียบกับการเขียนค่าโดยตรงกับการลงทะเบียน PORTnSET / PORTnCLEAR ของไมโครซึ่งสร้างความแตกต่างจริง ๆ พินของการสับเปลี่ยนความถี่ ISR
สำหรับผู้ที่ไม่คุ้นเคย: ในตัวอย่างของฉันไมโครมีการลงทะเบียน pin-state ทั่วไป PORTn ซึ่งสะท้อนถึงขาออกดังนั้นการทำ PORTn | = BIT_TO_SET ผลในการอ่าน - แก้ไข - เขียนลงทะเบียนที่ อย่างไรก็ตามการลงทะเบียน PORTnSET / PORTnCLEAR ใช้ '1' เพื่อหมายถึง "โปรดทำให้ bit นี้ 1" (SET) หรือ "โปรดทำให้ bit นี้เป็นศูนย์" (CLEAR) และ '0' เพื่อหมายถึง "ปล่อยให้อยู่คนเดียว" ดังนั้นคุณท้ายด้วยสองที่อยู่พอร์ตขึ้นอยู่กับว่าคุณตั้งค่ากำลังหรือล้างบิต (ไม่เสมอสะดวก) แต่มากปฏิกิริยาได้เร็วขึ้นและรหัสประกอบขนาดเล็ก
volatile
และดังนั้นคอมไพเลอร์ไม่สามารถทำการปรับให้เหมาะสมกับรหัสที่เกี่ยวข้องกับการลงทะเบียนดังกล่าว ดังนั้นจึงเป็นการดีที่จะแยกรหัสดังกล่าวออกและดูว่ามันจะเปิดออกในระดับแอสเซมเบลอร์ได้อย่างไร
วิธีบิตฟิลด์มีข้อดีอื่น ๆ ในเวทีที่ฝังตัว คุณสามารถกำหนด 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 ก่อน แต่สิ่งนี้อาจขึ้นอยู่กับการใช้งาน ตรวจสอบว่าฟิลด์ตัวจัดการคอมไพเลอร์ของคุณข้ามขอบเขตของไบต์ได้อย่างไร
จากนั้นคุณสามารถอ่านเขียนทดสอบค่าแต่ละตัวเหมือนก่อนหน้า
#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 เป็นไปได้ที่จะสร้างมาโครที่คล้ายกันสำหรับการตั้งค่าและการล้างบิต ความแตกต่างที่สำคัญของการแก้ปัญหานี้เมื่อเทียบกับที่อื่น ๆ ที่นี่คือมันทำงานได้ทุกที่ในตัวแปรชนิดใดก็ได้
ทั่วไปมากขึ้นสำหรับบิตแมปที่มีขนาดตามอำเภอใจ:
#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)))
CHAR_BIT
ถูกกำหนดไว้แล้วโดยlimits.h
ที่คุณไม่จำเป็นต้องใส่ในของคุณเองBITS
(และในความเป็นจริงคุณทำรหัสของคุณแย่ลงโดยการทำเช่นนั้น)
โปรแกรมนี้จะเปลี่ยนบิตข้อมูลใด ๆ จาก 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);
}
}
}
หากคุณกำลังทำบิดนิด ๆ หน่อย ๆ คุณอาจต้องการใช้มาสก์ซึ่งจะทำให้ทุกอย่างรวดเร็วขึ้น ฟังก์ชั่นต่อไปนี้เร็วมากและยังยืดหยุ่นได้ (ช่วยให้บิตสองบิตในแผนที่บิตขนาดใดก็ได้)
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 อาฉันรู้สึกว่าสงครามลุกเป็นไฟมา บน...).
ใช้สิ่งนี้:
int ToggleNthBit ( unsigned char n, int num )
{
if(num & (1 << n))
num &= ~(1 << n);
else
num |= (1 << n);
return num;
}
การขยาย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;
}
หากคุณต้องการดำเนินการทั้งหมดนี้ด้วยการเขียนโปรแกรม 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 และมีประโยชน์ในการเชื่อมโยงกันระหว่างโปรเซสเซอร์
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 เดือนได้พิสูจน์ว่าระบบเมนเฟรมที่เราได้รับข้อมูลนั้นในความเป็นจริงการทำงานผิดพลาด ความเรียบง่ายของบิตเรย์ทำให้มั่นใจในความถูกต้องสูงมาก - เทียบกับวิธีการค้นหา
bool
รายการ อาจเป็น 4 ไบต์สำหรับการตั้งค่า C89 ที่ใช้int
ในการติดตั้งbool
นี่คือมาโครบางตัวที่ฉันใช้:
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)
ตัวแปรที่ใช้
int value, pos;
ค่า - ข้อมูล
pos - ตำแหน่งของบิตที่เราสนใจตั้งค่าล้างหรือสลับ
ตั้งค่าบิต:
value = value | 1 << pos;
ล้างข้อมูลเล็กน้อย:
value = value & ~(1 << pos);
สลับบิต:
value = value ^ 1 << pos;
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
ให้สมมติว่าบางสิ่งแรกที่
num = 55
จำนวนเต็มเพื่อทำการดำเนินการระดับบิต (ตั้งรับล้างสลับ)
n = 4
0 ตำแหน่งบิตตามการดำเนินการระดับบิต
nth
บิตของการเปลี่ยนแปลงทางขวา NUM num
, n
ครั้ง จากนั้นดำเนินการ bitwise และ&
ด้วย 1bit = (num >> n) & 1;
มันทำงานอย่างไร?
0011 0111 (55 in decimal)
>> 4 (right shift 4 times)
-----------------
0000 0011
& 0000 0001 (1 in decimal)
-----------------
=> 0000 0001 (final result)
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)
n
ครั้งคือ1 << n
ครั้งคือ~ (1 << n)
ล้างและส่วนที่เหลือของบิตจะกลายเป็นชุดเช่น&
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 ^ 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)
การอ่านที่แนะนำ - การออกกำลังกายผู้ประกอบการระดับบิต
คุณจะตั้งค่าล้างและสลับบิตได้อย่างไร
เพื่อจัดการกับข้อผิดพลาดการเข้ารหัสทั่วไปเมื่อพยายามสร้างรูปแบบ:
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;
หรือค่อย ๆ ส่งเสริมโดยการบังคับให้การดำเนินการทางคณิตศาสตร์ที่เป็นอย่างน้อยที่กว้างเป็นประเภทของ1
number
number |= (number*0 + 1) << x;
เช่นเดียวกับกิจวัตรบิตมากที่สุดดีที่สุดที่จะทำงานร่วมกับที่ไม่ได้ลงชื่อประเภทมากกว่าการลงนามคน
number |= (type_of_number)1 << x;
มิได้number |= (number*0 + 1) << x;
จัดสรรเพื่อตั้งค่าบิตเครื่องหมายประเภทลงนาม ... number |= (1ull << x);
เป็นเรื่องของความเป็นจริงไม่เป็น มีวิธีพกพาที่จะทำตามตำแหน่ง?
รุ่น 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;}
}
;
คำจำกัดความฟังก์ชั่นของคุณทำไม?)
(variable & bits == bits)
?
((variable & bits) == bits)
std::bitset
ใน c ++ 11
โปรแกรมนี้ใช้วิธีการแก้ปัญหาข้างต้นของ @ 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
ลองใช้หนึ่งในฟังก์ชั่นเหล่านี้ในภาษา 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
อาจทำให้เกิดพฤติกรรมที่ไม่ได้กำหนด