Bagaimana Anda mengatur, menghapus, dan beralih sedikit?
Bagaimana Anda mengatur, menghapus, dan beralih sedikit?
Jawaban:
Gunakan bitwise OR operator ( |
) untuk mengatur bit.
number |= 1UL << n;
Itu akan mengatur n
bit th number
. n
harus nol, jika Anda ingin mengatur 1
bit st dan seterusnya n-1
, jika Anda ingin mengatur n
bit th.
Gunakan 1ULL
jika number
lebih luas dari unsigned long
; promosi 1UL << n
tidak terjadi sampai setelah mengevaluasi di 1UL << n
mana perilaku yang tidak ditentukan untuk bergeser lebih dari lebar a long
. Hal yang sama berlaku untuk semua contoh lainnya.
Gunakan bitwise AND operator ( &
) untuk menghapus sedikit.
number &= ~(1UL << n);
Itu akan menghapus n
sedikit number
. Anda harus membalikkan string bit dengan operator NOT bitwise ( ~
), lalu DAN itu.
Operator XOR ( ^
) dapat digunakan untuk beralih sedikit.
number ^= 1UL << n;
Itu akan beralih n
sedikit number
.
Anda tidak meminta ini, tetapi saya mungkin juga menambahkannya.
Untuk memeriksa sedikit, geser angka n ke kanan, lalu bitwise DAN itu:
bit = (number >> n) & 1U;
Itu akan menempatkan nilai n
bit number
ke dalam variabel bit
.
Mengatur n
bit th ke salah satu 1
atau 0
dapat dicapai dengan yang berikut pada implementasi C ++ 2 komplemen:
number ^= (-x ^ number) & (1UL << n);
Bit n
akan ditetapkan jika x
ini 1
, dan dibersihkan jika x
ini 0
. Jika x
memiliki nilai lain, Anda mendapatkan sampah. x = !!x
akan mendudukkannya menjadi 0 atau 1.
Untuk menjadikan ini tidak tergantung pada perilaku negasi komplemen 2 (di mana -1
semua bit diatur, tidak seperti pada komplemen 1 atau implementasi sign / magnitude C ++), gunakan negasi yang tidak ditandatangani.
number ^= (-(unsigned long)x ^ number) & (1UL << n);
atau
unsigned long newbit = !!x; // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);
Ini umumnya ide yang baik untuk menggunakan tipe yang tidak ditandatangani untuk manipulasi bit portabel.
atau
number = (number & ~(1UL << n)) | (x << n);
(number & ~(1UL << n))
akan menghapus n
bit th dan (x << n)
akan mengatur n
bit th ke x
.
Ini juga umumnya ide yang baik untuk tidak menyalin / menempel kode secara umum dan begitu banyak orang menggunakan macro preprocessor (seperti komunitas wiki menjawab lebih jauh ke bawah ) atau semacam enkapsulasi.
bit = (number >> x) & 1
1
adalah int
literal, yang ditandatangani. Jadi semua operasi di sini beroperasi pada angka yang ditandatangani, yang tidak didefinisikan dengan baik oleh standar. Standar tidak menjamin komplemen dua atau pergeseran aritmatika sehingga lebih baik digunakan 1U
.
number = number & ~(1 << n) | (x << n);
untuk Mengubah bit ke-x ke x.
Menggunakan Standard C ++ Library: std::bitset<N>
.
Atau Meningkatkan versi: boost::dynamic_bitset
.
Tidak perlu menggulung sendiri:
#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
Versi Boost memungkinkan bitet berukuran runtime dibandingkan dengan bitet berukuran waktu kompilasi pustaka standar .
Opsi lainnya adalah menggunakan bidang bit:
struct bits {
unsigned int a:1;
unsigned int b:1;
unsigned int c:1;
};
struct bits mybits;
mendefinisikan bidang 3-bit (sebenarnya, itu tiga bidang 1-bit). Operasi bit sekarang menjadi sedikit (haha) lebih sederhana:
Untuk mengatur atau menghapus sedikit:
mybits.b = 1;
mybits.c = 0;
Untuk beralih sedikit:
mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1; /* all work */
Memeriksa sedikit:
if (mybits.c) //if mybits.c is non zero the next line below will execute
Ini hanya bekerja dengan bidang bit ukuran tetap. Kalau tidak, Anda harus menggunakan teknik bit-twiddling yang dijelaskan dalam posting sebelumnya.
Saya menggunakan makro yang didefinisikan dalam file header untuk menangani bit yang diatur dan dihapus:
/* 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))
harus ((x) & (y)) == (y)
dinyatakan sebaliknya mengembalikan hasil yang salah pada multibit mask (mis. 5
vs. 3
) / * Halo untuk semua penggali kubur
1
harus (uintmax_t)1
serupa atau seandainya ada yang mencoba menggunakan makro ini pada long
tipe yang lebih besar
BITMASK_CHECK_ALL(x,y)
dapat diimplementasikan sebagai!~((~(y))|(x))
!(~(x) & (y))
Kadang-kadang layak digunakan enum
untuk menamai bit:
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
Kemudian gunakan nama nanti. Yaitu menulis
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
untuk mengatur, menghapus, dan menguji. Dengan cara ini Anda menyembunyikan angka ajaib dari sisa kode Anda.
Selain itu saya mendukung solusi Jeremy.
clearbits()
fungsi, bukan &= ~
. Mengapa Anda menggunakan enum untuk ini? Saya pikir itu untuk membuat banyak variabel unik dengan nilai arbitrase tersembunyi, tetapi Anda menetapkan nilai yang pasti untuk masing-masing. Jadi apa untungnya vs mendefinisikan mereka sebagai variabel?
enum
s untuk set konstanta terkait kembali pada pemrograman c. Saya menduga bahwa dengan kompiler modern satu-satunya keuntungan di atas const short
atau apa pun adalah bahwa mereka secara eksplisit dikelompokkan bersama. Dan ketika Anda ingin mereka untuk sesuatu yang lain daripada bitmasks Anda mendapatkan penomoran otomatis. Dalam c ++ tentu saja, mereka juga membentuk tipe berbeda yang memberi Anda sedikit tambahan pengecekan kesalahan statis.
enum ThingFlags
gunanya ThingError|ThingFlag1
, misalnya?
int
. Ini dapat menyebabkan segala macam bug halus karena promosi integer implisit atau operasi bitwise pada tipe yang ditandatangani. thingstate = ThingFlag1 >> 1
misalnya akan meminta perilaku yang ditentukan implementasi. thingstate = (ThingFlag1 >> x) << y
dapat memanggil perilaku yang tidak terdefinisi. Dan seterusnya. Agar aman, selalu dilemparkan ke jenis yang tidak ditandatangani.
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)))
OKE, mari kita menganalisis hal-hal ...
Ekspresi umum yang tampaknya Anda mengalami masalah dengan semua ini adalah "(1L << (posn))". Semua ini dilakukan adalah membuat topeng dengan bit tunggal dan yang akan bekerja dengan semua tipe integer. Argumen "posn" menentukan posisi di mana Anda menginginkan bit. Jika posn == 0, maka ungkapan ini akan mengevaluasi ke:
0000 0000 0000 0000 0000 0000 0000 0001 binary.
Jika posn == 8, itu akan mengevaluasi ke:
0000 0000 0000 0000 0000 0001 0000 0000 binary.
Dengan kata lain, itu hanya membuat bidang 0 dengan 1 pada posisi yang ditentukan. Satu-satunya bagian yang sulit adalah dalam makro BitClr () di mana kita perlu mengatur 0 bit tunggal dalam bidang 1 ini. Ini dicapai dengan menggunakan komplemen 1 dari ekspresi yang sama seperti dilambangkan oleh operator tilde (~).
Setelah mask dibuat, itu diterapkan pada argumen seperti yang Anda sarankan, dengan menggunakan bitwise dan (&), atau (|), dan xor (^) operator. Karena topeng bertipe panjang, makro akan bekerja dengan baik pada karakter char, short, int, atau long.
Intinya adalah bahwa ini adalah solusi umum untuk seluruh kelas masalah. Tentu saja mungkin dan bahkan pantas untuk menulis ulang ekuivalen dari makro ini dengan nilai mask eksplisit setiap kali Anda membutuhkannya, tetapi mengapa melakukannya? Ingat, substitusi makro terjadi di preprocessor dan kode yang dihasilkan akan mencerminkan fakta bahwa nilai-nilai dianggap konstan oleh kompiler - yaitu sama efisiennya dengan menggunakan makro umum untuk "menciptakan kembali roda" setiap kali Anda perlu melakukan manipulasi bit.
Tidak yakin? Berikut ini beberapa kode uji - Saya menggunakan Watcom C dengan optimisasi penuh dan tanpa menggunakan _cdecl sehingga pembongkaran yang dihasilkan akan sebersih mungkin:
---- [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 (dibongkar)] -------------------------------------- ---------
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
ada long long
. 1L
perlu jenis seluas mungkin, jadi (uintmax_t)1
. (Anda mungkin lolos 1ull
)
Gunakan operator bitwise: &
|
Untuk mengatur bit terakhir di 000b
:
foo = foo | 001b
Untuk memeriksa bit terakhir di foo
:
if ( foo & 001b ) ....
Untuk menghapus bit terakhir di foo
:
foo = foo & 110b
Saya menggunakan XXXb
untuk kejelasan. Anda mungkin akan bekerja dengan representasi HEX, tergantung pada struktur data tempat Anda mengemas bit.
foo = foo ^ MY_MASK
foo = foo & ~MY_MASK
Untuk pemula, saya ingin menjelaskan sedikit lebih banyak dengan contoh:
Contoh:
value is 0x55;
bitnum : 3rd.
The &
operator yang digunakan memeriksa 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)
Beralih atau Balik:
0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)
|
operator: atur bitnya
0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)
Inilah makro aritmatika bit favorit saya, yang berfungsi untuk semua jenis bilangan bulat tak bertanda dari unsigned char
hingga size_t
(yang merupakan jenis terbesar yang harus efisien untuk bekerja dengannya):
#define BITOP(a,b,op) \
((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))
Untuk mengatur sedikit:
BITOP(array, bit, |=);
Untuk menghapus sedikit:
BITOP(array, bit, &=~);
Untuk beralih sedikit:
BITOP(array, bit, ^=);
Untuk menguji sedikit:
if (BITOP(array, bit, &)) ...
dll.
BITOP(array, bit++, |=);
dalam satu lingkaran kemungkinan besar tidak akan melakukan apa yang diinginkan pemanggil.
BITCELL(a,b) |= BITMASK(a,b);
(keduanya mengambil a
sebagai argumen untuk menentukan ukuran, tetapi yang terakhir tidak akan pernah mengevaluasi a
karena hanya muncul di sizeof
).
(size_t)
pemain tampaknya berada di sana hanya untuk memastikan beberapa matematika unsigned dengan %
. Bisa di (unsigned)
sana
(size_t)(b)/(8*sizeof *(a))
tidak perlu bisa menyempit b
sebelum divisi. Hanya masalah dengan bit array yang sangat besar. Masih makro yang menarik.
Karena ini ditandai "tertanam" Saya akan menganggap Anda menggunakan mikrokontroler. Semua saran di atas adalah valid & berfungsi (baca-modifikasi-tulis, serikat pekerja, struct, dll.).
Namun, selama serangan debugging berbasis osiloskop saya kagum menemukan bahwa metode ini memiliki overhead yang cukup besar dalam siklus CPU dibandingkan dengan menulis nilai langsung ke register PORTnSET / PORTnCLEAR mikro yang membuat perbedaan nyata di mana ada loop ketat / tinggi Pin pengalih frekuensi ISR.
Bagi mereka yang tidak terbiasa: Dalam contoh saya, mikro memiliki register umum pin-state PORTn yang mencerminkan pin keluaran, sehingga melakukan PORTn | = BIT_TO_SET menghasilkan baca-modifikasi-tulis ke register itu. Namun, register PORTnSET / PORTnCLEAR mengambil '1' yang berarti "tolong buat bit ini 1" (SET) atau "tolong buat bit ini nol" (CLEAR) dan tanda '0' berarti "biarkan pin sendirian". jadi, Anda berakhir dengan dua alamat port tergantung apakah Anda mengatur atau menghapus bit (tidak selalu nyaman) tetapi reaksi yang jauh lebih cepat dan kode rakitan yang lebih kecil.
volatile
dan karena itu kompiler tidak dapat melakukan optimasi pada kode yang melibatkan register tersebut. Oleh karena itu, praktik yang baik untuk membongkar kode tersebut dan melihat bagaimana hasilnya pada tingkat assembler.
Pendekatan bitfield memiliki kelebihan lain di arena tertanam. Anda dapat mendefinisikan struct yang memetakan langsung ke bit dalam register perangkat keras tertentu.
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;
Anda harus mengetahui urutan pengepakan bit - Saya pikir ini MSB pertama, tetapi ini mungkin tergantung pada implementasi. Juga, verifikasi bagaimana kompiler Anda menangani bidang melintasi batas byte.
Anda kemudian dapat membaca, menulis, menguji nilai-nilai individual seperti sebelumnya.
#define bit_test(x, y) ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )
Penggunaan sampel:
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;
}
Catatan: Ini dirancang agar cepat (mengingat fleksibilitasnya) dan tidak bercabang. Ini menghasilkan kode mesin SPARC yang efisien saat dikompilasi Sun Studio 8; Saya juga sudah mengujinya menggunakan MSVC ++ 2008 pada amd64. Dimungkinkan untuk membuat makro yang sama untuk pengaturan dan menghapus bit. Perbedaan utama dari solusi ini dibandingkan dengan banyak yang lain di sini adalah bahwa ia berfungsi untuk semua lokasi di hampir semua jenis variabel.
Lebih umum, untuk bitmap berukuran acak:
#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
sudah ditentukan oleh limits.h
, Anda tidak perlu memasukkannya sendiri BITS
(dan faktanya membuat kode Anda lebih buruk dengan melakukannya)
Program ini untuk mengubah bit data apa saja dari 0 menjadi 1 atau 1 menjadi 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);
}
}
}
Jika Anda melakukan banyak twiddling, Anda mungkin ingin menggunakan masker yang akan membuat semuanya lebih cepat. Fungsi-fungsi berikut sangat cepat dan masih fleksibel (mereka memungkinkan sedikit memutar-mutar peta bit dari berbagai ukuran).
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;
}
Catatan, untuk mengatur bit 'n' dalam bilangan bulat 16 bit Anda melakukan hal berikut:
TSetBit( n, &my_int);
Terserah Anda untuk memastikan bahwa nomor bit berada dalam kisaran peta bit yang Anda lewati. Perhatikan bahwa untuk prosesor endian kecil yang byte, kata-kata, kata-kata, kata-kata, dll., Memetakan dengan benar satu sama lain dalam memori (alasan utama bahwa prosesor endian kecil 'lebih baik' dari prosesor big-endian, ah, saya merasakan perang api datang di...).
Gunakan ini:
int ToggleNthBit ( unsigned char n, int num )
{
if(num & (1 << n))
num &= ~(1 << n);
else
num |= (1 << n);
return num;
}
Memperluas bitset
jawaban:
#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;
}
Jika Anda ingin melakukan semua operasi ini dengan pemrograman C di kernel Linux maka saya sarankan untuk menggunakan API standar dari kernel Linux.
Lihat 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
Catatan: Di sini seluruh operasi terjadi dalam satu langkah. Jadi ini semua dijamin atom bahkan di komputer SMP dan berguna untuk menjaga koherensi di seluruh prosesor.
Visual C 2010, dan mungkin banyak kompiler lain, memiliki dukungan langsung untuk operasi boolean bawaan. Sedikit memiliki dua nilai yang mungkin, seperti halnya boolean, jadi kita dapat menggunakan boolean sebagai gantinya - bahkan jika mereka mengambil lebih banyak ruang daripada satu bit di memori dalam representasi ini. Ini berfungsi, bahkan sizeof()
operator bekerja dengan baik.
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);
}
Jadi, untuk pertanyaan Anda IsGph[i] =1
,, atau IsGph[i] =0
buat pengaturan dan pembersihan bools menjadi mudah.
Untuk menemukan karakter yang tidak patut dicetak:
// 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;
}
}
Perhatikan tidak ada "khusus" tentang kode ini. Ini memperlakukan sedikit seperti bilangan bulat - yang secara teknis, itu. Bilangan bulat 1 bit yang dapat menampung 2 nilai, dan 2 nilai saja.
Saya pernah menggunakan pendekatan ini untuk menemukan catatan pinjaman duplikat, di mana nomor pinjaman adalah kunci ISAM, menggunakan nomor pinjaman 6 digit sebagai indeks ke dalam bit array. Sangat cepat, dan setelah 8 bulan, membuktikan bahwa sistem mainframe yang kami peroleh data ternyata tidak berfungsi. Kesederhanaan bit array membuat kepercayaan pada kebenarannya sangat tinggi - vs pendekatan pencarian misalnya.
bool
. Mungkin bahkan 4 byte untuk pengaturan C89 yang digunakan int
untuk mengimplementasikanbool
Berikut beberapa makro yang saya gunakan:
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)
Variabel yang digunakan
int value, pos;
value - Data
pos - posisi bit yang ingin kami atur, hapus atau alihkan.
Tetapkan sedikit:
value = value | 1 << pos;
Hapus sedikit:
value = value & ~(1 << pos);
Beralih sedikit:
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
bisa bool
.
Misalkan beberapa hal pertama
num = 55
Integer untuk melakukan operasi bitwise (set, get, clear, toggle).
n = 4
Posisi bit berbasis 0 untuk melakukan operasi bitwise.
nth
sedikit pergeseran kanan num
, n
kali. Kemudian lakukan bitwise AND &
dengan 1.bit = (num >> n) & 1;
Bagaimana itu bekerja?
0011 0111 (55 in decimal)
>> 4 (right shift 4 times)
-----------------
0000 0011
& 0000 0001 (1 in decimal)
-----------------
=> 0000 0001 (final result)
n
kali. Kemudian lakukan operasi ATAU bitwise |
dengan num
.num |= (1 << n); // Equivalent to; num = (1 << n) | num;
Bagaimana itu bekerja?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
0001 0000
| 0011 0111 (55 in decimal)
-----------------
=> 0001 0000 (final result)
n
kali yaitu 1 << n
.~ (1 << n)
.&
operasi bitwise DAN dengan hasil di atas dan num
. Tiga langkah di atas bersama-sama dapat ditulis sebagai num & (~ (1 << n))
;num &= (~(1 << n)); // Equivalent to; num = num & (~(1 << n));
Bagaimana itu bekerja?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
~ 0001 0000
-----------------
1110 1111
& 0011 0111 (55 in decimal)
-----------------
=> 0010 0111 (final result)
Untuk beralih sedikit kami menggunakan ^
operator XOR bitwise . Operator Bitwise XOR mengevaluasi ke 1 jika bit yang sesuai dari kedua operan berbeda, jika tidak dievaluasi menjadi 0.
Yang berarti untuk beralih sedikit, kita perlu melakukan operasi XOR dengan bit yang ingin Anda toggle dan 1.
num ^= (1 << n); // Equivalent to; num = num ^ (1 << n);
Bagaimana itu bekerja?
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)
Dianjurkan membaca - Latihan operator Bitwise
Bagaimana Anda mengatur, menghapus, dan beralih sedikit pun?
Untuk mengatasi perangkap kode yang umum ketika mencoba untuk membentuk topeng:
1
tidak selalu cukup lebar
Masalah apa yang terjadi ketika number
tipe yang lebih luas dari itu 1
?
x
mungkin terlalu bagus untuk perubahan 1 << x
menuju perilaku tidak terdefinisi (UB). Sekalipun x
tidak terlalu hebat, ~
mungkin tidak cukup membalik bit paling signifikan.
// 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
Untuk memastikan 1 cukup lebar:
Kode dapat digunakan 1ull
atau dengan pedantik (uintmax_t)1
dan membiarkan kompiler mengoptimalkan.
number |= (1ull << x);
number |= ((uintmax_t)1 << x);
Atau para pemain - yang membuat masalah pengkodean / review / pemeliharaan menjaga para pemain agar tetap benar dan terbaru.
number |= (type_of_number)1 << x;
Atau dengan lembut mempromosikan 1
dengan memaksa operasi matematika yang paling tidak selebar jenis number
.
number |= (number*0 + 1) << x;
Seperti kebanyakan sedikit manipulasi, terbaik untuk bekerja dengan unsigned jenis daripada ditandatangani yang
number |= (type_of_number)1 << x;
juga tidak number |= (number*0 + 1) << x;
tepat untuk mengatur bit tanda dari jenis yang ditandatangani ... Sebenarnya, tidak juga number |= (1ull << x);
. Apakah ada cara portabel untuk melakukannya dengan posisi?
Versi templat C ++ 11 (dimasukkan dalam header):
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;}
}
;
setelah definisi fungsi Anda?)
(variable & bits == bits)
?
((variable & bits) == bits)
std::bitset
di c ++ 11
Program ini didasarkan pada solusi @ Jeremy di atas. Jika seseorang ingin bermain-main dengan cepat.
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
Coba salah satu dari fungsi ini dalam bahasa C untuk mengubah n bit:
char bitfield;
// Start at 0th position
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}
Atau
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}
Atau
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
dapat menyebabkan perilaku yang tidak terdefinisi