Làm thế nào để bạn thiết lập, xóa và chuyển đổi một chút?
Làm thế nào để bạn thiết lập, xóa và chuyển đổi một chút?
Câu trả lời:
Sử dụng toán tử bitwise OR ( |
) để đặt bit.
number |= 1UL << n;
Điều đó sẽ thiết lập n
bit thứ number
. n
nên bằng 0, nếu bạn muốn đặt 1
bit st và cứ thế n-1
, nếu bạn muốn đặt n
bit thứ.
Sử dụng 1ULL
nếu number
rộng hơn unsigned long
; quảng cáo 1UL << n
không xảy ra cho đến khi sau khi đánh giá 1UL << n
hành vi không xác định của nó sẽ thay đổi nhiều hơn chiều rộng của a long
. Điều tương tự áp dụng cho tất cả các ví dụ còn lại.
Sử dụng toán tử bitwise AND ( &
) để xóa một chút.
number &= ~(1UL << n);
Điều đó sẽ xóa n
bit thứ number
. Bạn phải đảo ngược chuỗi bit với toán tử bitwise NOT ( ~
), sau đó VÀ nó.
Toán tử XOR ( ^
) có thể được sử dụng để chuyển đổi một chút.
number ^= 1UL << n;
Điều đó sẽ thay đổi n
bit thứ number
.
Bạn đã không yêu cầu điều này, nhưng tôi cũng có thể thêm nó.
Để kiểm tra một chút, dịch chuyển số n sang phải, sau đó theo bit VÀ nó:
bit = (number >> n) & 1U;
Điều đó sẽ đặt giá trị của n
bit thứ number
vào biến bit
.
Đặt n
bit thứ một 1
hoặc 0
có thể đạt được bằng cách sau khi triển khai C ++ bổ sung của 2:
number ^= (-x ^ number) & (1UL << n);
Bit n
sẽ được đặt nếu x
là 1
và xóa nếu x
có 0
. Nếu x
có một số giá trị khác, bạn nhận được rác. x = !!x
sẽ booleanize nó thành 0 hoặc 1.
Để làm cho điều này độc lập với hành vi phủ định bổ sung của 2 (trong đó -1
có tất cả các bit được đặt, không giống như thực hiện C ++ bổ sung hoặc ký hiệu / cường độ 1), hãy sử dụng phủ định không dấu.
number ^= (-(unsigned long)x ^ number) & (1UL << n);
hoặc là
unsigned long newbit = !!x; // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);
Nói chung, nên sử dụng các loại không dấu cho thao tác bit di động.
hoặc là
number = (number & ~(1UL << n)) | (x << n);
(number & ~(1UL << n))
sẽ xóa n
bit thứ và (x << n)
sẽ đặt n
bit thứ x
.
Nói chung, một ý tưởng tốt là không sao chép / dán mã nói chung và rất nhiều người sử dụng các macro tiền xử lý (như wiki cộng đồng trả lời sâu hơn ) hoặc một số cách đóng gói.
bit = (number >> x) & 1
1
là một int
nghĩa đen, được ký kết. Vì vậy, tất cả các hoạt động ở đây hoạt động trên các số đã ký, không được xác định rõ bởi các tiêu chuẩn. Các tiêu chuẩn không đảm bảo hai phép bổ sung hoặc thay đổi số học nên sử dụng tốt hơn 1U
.
number = number & ~(1 << n) | (x << n);
thay đổi bit thứ n thành x.
Sử dụng Thư viện C ++ chuẩn : std::bitset<N>
.
Hoặc phiên bản Boost : boost::dynamic_bitset
.
Không cần phải tự cuộn:
#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
Phiên bản Boost cho phép một bitet có kích thước thời gian chạy so với một bitet có kích thước thời gian biên dịch thư viện chuẩn .
Tùy chọn khác là sử dụng các trường bit:
struct bits {
unsigned int a:1;
unsigned int b:1;
unsigned int c:1;
};
struct bits mybits;
định nghĩa trường 3 bit (thực ra, đó là ba feld 1 bit). Hoạt động bit bây giờ trở nên đơn giản hơn một chút (haha):
Để thiết lập hoặc xóa một chút:
mybits.b = 1;
mybits.c = 0;
Để chuyển đổi một chút:
mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1; /* all work */
Kiểm tra một chút:
if (mybits.c) //if mybits.c is non zero the next line below will execute
Điều này chỉ hoạt động với các trường bit kích thước cố định. Nếu không, bạn phải sử dụng các kỹ thuật xoay vòng bit được mô tả trong các bài viết trước.
Tôi sử dụng các macro được xác định trong tệp tiêu đề để xử lý tập bit và xóa:
/* 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))
phải là ((x) & (y)) == (y)
cách khác, nó trả về kết quả không chính xác trên mặt nạ multibit (ví dụ 5
so với 3
) / * Xin chào tất cả các
1
nên (uintmax_t)1
hoặc tương tự trong trường hợp bất kỳ ai cố gắng sử dụng các macro này trên một loại long
hoặc lớn hơn
BITMASK_CHECK_ALL(x,y)
có thể được thực hiện như!~((~(y))|(x))
!(~(x) & (y))
Đôi khi đáng để sử dụng một enum
để đặt tên cho các bit:
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
Sau đó sử dụng tên sau này. Tức là viết
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
để thiết lập, xóa và kiểm tra. Bằng cách này bạn ẩn các số ma thuật khỏi phần còn lại của mã của bạn.
Ngoài ra, tôi tán thành giải pháp của Jeremy.
clearbits()
chức năng thay vì &= ~
. Tại sao bạn sử dụng một enum cho điều này? Tôi nghĩ rằng chúng là để tạo ra một loạt các biến duy nhất có giá trị tùy ý ẩn, nhưng bạn đang gán một giá trị xác định cho mỗi biến. Vì vậy, lợi ích so với chỉ định nghĩa chúng là các biến?
enum
s cho các bộ hằng số liên quan trở lại một chặng đường dài trong lập trình c. Tôi nghi ngờ rằng với các trình biên dịch hiện đại, lợi thế duy nhất hơn const short
hoặc bất cứ điều gì là chúng được nhóm rõ ràng với nhau. Và khi bạn muốn họ cho một cái gì đó khác hơn bitmasks bạn sẽ có được đánh số tự động. Tất nhiên, trong c ++, chúng cũng tạo thành các loại riêng biệt giúp bạn kiểm tra lỗi tĩnh thêm một chút.
enum ThingFlags
giá trị cho ThingError|ThingFlag1
, cho ví dụ?
int
. Điều này có thể gây ra tất cả các lỗi tinh vi do quảng cáo số nguyên ẩn hoặc hoạt động theo bit trên các loại đã ký. thingstate = ThingFlag1 >> 1
ví dụ sẽ gọi hành vi xác định thực hiện. thingstate = (ThingFlag1 >> x) << y
có thể gọi hành vi không xác định. Và như thế. Để an toàn, luôn luôn chuyển sang loại không dấu.
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)))
OK, hãy phân tích mọi thứ ...
Biểu thức phổ biến mà bạn dường như gặp vấn đề trong tất cả những điều này là "(1L << (posn))". Tất cả điều này là tạo một mặt nạ với một bit duy nhất và nó sẽ hoạt động với bất kỳ loại số nguyên nào. Đối số "posn" chỉ định vị trí mà bạn muốn bit. Nếu posn == 0, thì biểu thức này sẽ ước tính thành:
0000 0000 0000 0000 0000 0000 0000 0001 binary.
Nếu posn == 8, nó sẽ ước tính:
0000 0000 0000 0000 0000 0001 0000 0000 binary.
Nói cách khác, nó chỉ đơn giản tạo ra một trường 0 'với 1 tại vị trí đã chỉ định. Phần khó khăn duy nhất là trong macro BitClr () nơi chúng ta cần đặt 0 bit đơn trong trường 1 giây. Điều này được thực hiện bằng cách sử dụng phần bù 1 của cùng biểu thức như được biểu thị bởi toán tử dấu ngã (~).
Khi mặt nạ được tạo, nó được áp dụng cho đối số như bạn đề xuất, bằng cách sử dụng các toán tử bitwise và (&) hoặc (|) và xor (^). Vì mặt nạ có kiểu dài, các macro sẽ hoạt động tốt như trên char, short, int hoặc long.
Điểm mấu chốt là đây là một giải pháp chung cho toàn bộ lớp vấn đề. Tất nhiên, có thể và thậm chí thích hợp để viết lại tương đương với bất kỳ macro nào có giá trị mặt nạ rõ ràng mỗi khi bạn cần, nhưng tại sao lại làm như vậy? Hãy nhớ rằng, sự thay thế macro xảy ra trong bộ tiền xử lý và do đó mã được tạo sẽ phản ánh thực tế rằng các giá trị được trình biên dịch coi là không đổi - tức là sử dụng các macro tổng quát như là "phát minh lại bánh xe" mỗi khi bạn cần làm thao tác bit.
Không tin tưởng? Đây là một số mã kiểm tra - Tôi đã sử dụng Watcom C với tối ưu hóa hoàn toàn và không sử dụng _cdecl để việc tháo gỡ kết quả sẽ sạch nhất có thể:
---- [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;
}
---- [TEST.OUT (tháo rời)] -------------------------------------- ---------
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
---- [vây] ------------------------------------------- ----------------------
arg
là long long
. 1L
cần phải là loại rộng nhất có thể, vì vậy (uintmax_t)1
. (Bạn có thể thoát khỏi 1ull
)
Sử dụng các toán tử bitwise: &
|
Để đặt bit cuối cùng trong 000b
:
foo = foo | 001b
Để kiểm tra bit cuối cùng trong foo
:
if ( foo & 001b ) ....
Để xóa bit cuối cùng trong foo
:
foo = foo & 110b
Tôi đã sử dụng XXXb
cho rõ ràng. Bạn có thể sẽ làm việc với đại diện HEX, tùy thuộc vào cấu trúc dữ liệu mà bạn đang đóng gói bit.
foo = foo ^ MY_MASK
foo = foo & ~MY_MASK
Đối với người mới bắt đầu, tôi muốn giải thích thêm một chút với một ví dụ:
Thí dụ:
value is 0x55;
bitnum : 3rd.
Các &
nhà điều hành được sử dụng kiểm tra các bit:
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)
Chuyển đổi hoặc lật:
0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)
|
toán tử: đặt bit
0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)
Đây là macro số học bit yêu thích của tôi, hoạt động cho bất kỳ loại mảng số nguyên không dấu nào từ unsigned char
tối đa size_t
(là loại lớn nhất cần hiệu quả để làm việc):
#define BITOP(a,b,op) \
((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))
Để thiết lập một chút:
BITOP(array, bit, |=);
Để xóa một chút:
BITOP(array, bit, &=~);
Để chuyển đổi một chút:
BITOP(array, bit, ^=);
Để kiểm tra một chút:
if (BITOP(array, bit, &)) ...
Vân vân.
BITOP(array, bit++, |=);
trong một vòng lặp rất có thể sẽ không làm những gì người gọi muốn.
BITCELL(a,b) |= BITMASK(a,b);
(cả mất a
như một tham số để xác định kích thước, nhưng sau này sẽ không bao giờ đánh giá a
từ nó chỉ xuất hiện trong sizeof
).
(size_t)
diễn viên dường như có mặt ở đó chỉ để đảm bảo một số toán unsigned với %
. Có thể (unsigned)
có.
(size_t)(b)/(8*sizeof *(a))
cần thiết có thể thu hẹp b
trước khi phân chia. Chỉ có một vấn đề với mảng bit rất lớn. Vẫn là một macro thú vị.
Vì điều này được gắn thẻ "nhúng" Tôi sẽ cho rằng bạn đang sử dụng một vi điều khiển. Tất cả các đề xuất trên là hợp lệ và công việc (đọc-sửa-ghi, đoàn thể, cấu trúc, v.v.).
Tuy nhiên, trong quá trình gỡ lỗi dựa trên dao động, tôi đã rất ngạc nhiên khi thấy rằng các phương thức này có chi phí đáng kể trong chu kỳ CPU so với việc ghi giá trị trực tiếp vào các thanh ghi PORTnSET / PORTnCLEAR của micro tạo ra sự khác biệt thực sự khi có các vòng lặp chặt chẽ / cao -frequency ISR's ghim ghim.
Đối với những người không quen thuộc: Trong ví dụ của tôi, micro có PORTn trạng thái pin chung chung phản ánh các chân đầu ra, do đó, PORTn | = BIT_TO_SET dẫn đến việc đọc-sửa đổi-ghi vào thanh ghi đó. Tuy nhiên, các thanh ghi PORTnSET / PORTnCLEAR lấy '1' có nghĩa là "vui lòng tạo bit này 1" (SET) hoặc "vui lòng làm cho bit này bằng 0" (XÓA) và '0' có nghĩa là "để yên pin". do đó, bạn kết thúc với hai địa chỉ cổng tùy thuộc vào việc bạn đang cài đặt hoặc xóa bit (không phải lúc nào cũng thuận tiện) nhưng phản ứng nhanh hơn nhiều và mã được lắp ráp nhỏ hơn.
volatile
và do đó trình biên dịch không thể thực hiện bất kỳ tối ưu hóa nào trên mã liên quan đến các thanh ghi đó. Do đó, cách tốt nhất là tháo rời mã như vậy và xem cách nó bật ra ở cấp độ trình biên dịch.
Cách tiếp cận bitfield có những lợi thế khác trong lĩnh vực nhúng. Bạn có thể định nghĩa một cấu trúc ánh xạ trực tiếp lên các bit trong một thanh ghi phần cứng cụ thể.
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;
Bạn cần lưu ý về thứ tự đóng gói bit - Tôi nghĩ đó là MSB trước tiên, nhưng điều này có thể phụ thuộc vào việc triển khai. Ngoài ra, xác minh cách trình biên dịch của bạn xử lý các trường vượt qua ranh giới byte.
Sau đó, bạn có thể đọc, viết, kiểm tra các giá trị riêng lẻ như trước đây.
#define bit_test(x, y) ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )
Sử dụng mẫu:
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;
}
Ghi chú: Điều này được thiết kế để nhanh chóng (do tính linh hoạt của nó) và không phân nhánh. Nó dẫn đến mã máy SPARC hiệu quả khi biên dịch Sun Studio 8; Tôi cũng đã thử nghiệm nó bằng MSVC ++ 2008 trên amd64. Có thể tạo các macro tương tự để thiết lập và xóa bit. Sự khác biệt chính của giải pháp này so với nhiều giải pháp khác ở đây là nó hoạt động cho bất kỳ vị trí nào trong hầu hết mọi loại biến.
Tổng quát hơn, đối với bitmap có kích thước tùy ý:
#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
đã được xác định bởi limits.h
, bạn không cần phải tự đặt BITS
(và thực tế là bạn làm cho mã của mình tệ hơn bằng cách làm như vậy)
Chương trình này là để thay đổi bất kỳ bit dữ liệu nào từ 0 thành 1 hoặc 1 thành 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);
}
}
}
Nếu bạn đang thực hiện nhiều thao tác xoay vòng, bạn có thể muốn sử dụng mặt nạ sẽ giúp mọi việc nhanh hơn. Các chức năng sau rất nhanh và vẫn linh hoạt (chúng cho phép xoay một chút trong các bản đồ bit có kích thước bất kỳ).
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;
}
Lưu ý, để đặt bit 'n' trong số nguyên 16 bit, bạn làm như sau:
TSetBit( n, &my_int);
Tùy thuộc vào bạn để đảm bảo rằng số bit nằm trong phạm vi của bản đồ bit mà bạn vượt qua. Lưu ý rằng đối với các bộ xử lý endian nhỏ mà byte, từ, dwords, qwords, v.v., ánh xạ chính xác với nhau trong bộ nhớ (lý do chính khiến các bộ xử lý endian nhỏ 'tốt hơn' so với bộ xử lý endian lớn, ah, tôi cảm thấy một cuộc chiến nảy lửa sắp xảy ra trên...).
Dùng cái này:
int ToggleNthBit ( unsigned char n, int num )
{
if(num & (1 << n))
num &= ~(1 << n);
else
num |= (1 << n);
return num;
}
Mở rộng về bitset
câu trả lời:
#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;
}
Nếu bạn muốn thực hiện tất cả thao tác này với lập trình C trong nhân Linux thì tôi khuyên bạn nên sử dụng các API tiêu chuẩn của nhân Linux.
Xem 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
Lưu ý: Ở đây toàn bộ hoạt động xảy ra trong một bước duy nhất. Vì vậy, tất cả những thứ này được đảm bảo là nguyên tử ngay cả trên máy tính SMP và rất hữu ích để giữ sự gắn kết giữa các bộ xử lý.
Visual C 2010, và có lẽ nhiều trình biên dịch khác, có hỗ trợ trực tiếp cho các hoạt động boolean được tích hợp. Một bit có hai giá trị có thể, giống như boolean, vì vậy chúng ta có thể sử dụng booleans thay thế - ngay cả khi chúng chiếm nhiều không gian hơn một bit trong bộ nhớ trong đại diện này. Điều này hoạt động, thậm chí các sizeof()
nhà điều hành hoạt động đúng.
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);
}
Vì vậy, đối với câu hỏi của bạn IsGph[i] =1
, hoặc IsGph[i] =0
làm cho thiết lập và xóa bool dễ dàng.
Để tìm các ký tự không thể in:
// 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;
}
}
Lưu ý không có gì "đặc biệt" về mã này. Nó xử lý một chút giống như một số nguyên - về mặt kỹ thuật, nó là. Một số nguyên 1 bit có thể chứa 2 giá trị và chỉ 2 giá trị.
Tôi đã từng sử dụng phương pháp này để tìm các hồ sơ cho vay trùng lặp, trong đó loan_number là khóa ISAM, sử dụng số cho vay gồm 6 chữ số làm chỉ mục vào mảng bit. Rất nhanh, và sau 8 tháng, đã chứng minh rằng hệ thống máy tính lớn mà chúng tôi nhận được dữ liệu trên thực tế đã gặp trục trặc. Sự đơn giản của mảng bit làm cho độ tin cậy về tính chính xác của chúng rất cao - ví dụ so với phương pháp tìm kiếm.
bool
. Thậm chí có thể 4 byte cho các thiết lập C89 sử dụng int
để thực hiệnbool
Dưới đây là một số macro tôi sử dụng:
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)
Biến được sử dụng
int value, pos;
giá trị - Vị
trí dữ liệu - vị trí của bit mà chúng ta quan tâm để đặt, xóa hoặc chuyển đổi.
Đặt một chút:
value = value | 1 << pos;
Xóa một chút:
value = value & ~(1 << pos);
Chuyển đổi một chút:
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
có thể bool
.
Giả sử vài thứ đầu tiên
num = 55
Integer thực hiện các thao tác bitwise (đặt, nhận, xóa, chuyển đổi).
n = 4
0 dựa trên vị trí bit để thực hiện các hoạt động bitwise.
nth
bit của num đúng num
, n
lần. Sau đó thực hiện bitwise AND &
với 1.bit = (num >> n) & 1;
Làm thế nào nó hoạt động?
0011 0111 (55 in decimal)
>> 4 (right shift 4 times)
-----------------
0000 0011
& 0000 0001 (1 in decimal)
-----------------
=> 0000 0001 (final result)
n
lần. Sau đó thực hiện thao tác bitwise HOẶC |
với num
.num |= (1 << n); // Equivalent to; num = (1 << n) | num;
Làm thế nào nó hoạt động?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
0001 0000
| 0011 0111 (55 in decimal)
-----------------
=> 0001 0000 (final result)
n
lần tức là 1 << n
.~ (1 << n)
.&
với kết quả trên và num
. Ba bước trên có thể được viết là num & (~ (1 << n))
;num &= (~(1 << n)); // Equivalent to; num = num & (~(1 << n));
Làm thế nào nó hoạt động?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
~ 0001 0000
-----------------
1110 1111
& 0011 0111 (55 in decimal)
-----------------
=> 0010 0111 (final result)
Để chuyển đổi một chút, chúng tôi sử dụng toán ^
tử XOR bitwise . Toán tử Bitwise XOR ước tính thành 1 nếu bit tương ứng của cả hai toán hạng khác nhau, nếu không thì ước tính là 0.
Có nghĩa là để chuyển đổi một chút, chúng ta cần thực hiện thao tác XOR với bit bạn muốn chuyển đổi và 1.
num ^= (1 << n); // Equivalent to; num = num ^ (1 << n);
Làm thế nào nó hoạt động?
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)
Đề nghị đọc - Bài tập toán tử bitwise
Làm thế nào để bạn thiết lập, xóa và chuyển đổi một bit?
Để giải quyết một cạm bẫy mã hóa phổ biến khi cố gắng tạo mặt nạ:
1
không phải lúc nào cũng đủ rộng
Vấn đề gì xảy ra khi number
một loại rộng hơn 1
?
x
có thể quá tuyệt vời cho sự thay đổi 1 << x
dẫn đến hành vi không xác định (UB). Ngay cả khi x
không quá lớn, ~
có thể không lật đủ các bit quan trọng nhất.
// 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
Để đảm bảo 1 đủ rộng:
Mã có thể sử dụng 1ull
hoặc theo phương pháp sư phạm (uintmax_t)1
và để trình biên dịch tối ưu hóa.
number |= (1ull << x);
number |= ((uintmax_t)1 << x);
Hoặc diễn viên - điều này làm cho các vấn đề về mã hóa / đánh giá / bảo trì giữ cho diễn viên chính xác và cập nhật.
number |= (type_of_number)1 << x;
Hoặc nhẹ nhàng thúc đẩy 1
bằng cách buộc một phép toán có độ rộng tối thiểu bằng loại number
.
number |= (number*0 + 1) << x;
Như với hầu hết các thao tác bit, tốt nhất để làm việc với unsigned loại chứ không phải ký kết những
number |= (type_of_number)1 << x;
phải cũng không number |= (number*0 + 1) << x;
thích hợp để đặt bit dấu của loại đã ký ... Như một vấn đề thực tế, cũng không phải number |= (1ull << x);
. Có một cách di động để làm điều đó theo vị trí?
Phiên bản templated C ++ 11 (đặt trong tiêu đề):
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;}
}
;
sau định nghĩa chức năng của mình?)
(variable & bits == bits)
?
((variable & bits) == bits)
std::bitset
trong c ++ 11
Chương trình này dựa trên giải pháp trên của @ Jeremy. Nếu ai đó muốn nhanh chóng chơi xung quanh.
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
Hãy thử một trong các chức năng này trong ngôn ngữ C để thay đổi n bit:
char bitfield;
// Start at 0th position
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}
Hoặc là
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}
Hoặc là
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
có thể gây ra hành vi không xác định