Bagaimana anda mengatur, menghapus, dan beralih sedikit?
Menggunakan bitwise operator OR (|
) untuk mengatur sedikit.
number |= 1UL << n;
Yang akan mengatur n berarti sedikit
nomor.
nharus nol, jika anda ingin mengatur
1'st sedikit dan sebagainya upto n-1
, jika anda ingin mengatur `n'th sedikit.
Gunakan 1ULL
jika jumlah
yang lebih luas dari unsigned long
; promosi 1UL << n
doesn't terjadi sampai setelah mengevaluasi 1UL << n
mana's perilaku tidak terdefinisi untuk menggeser oleh lebih dari lebar yang lama
. Hal yang sama berlaku untuk semua sisa contoh.
Menggunakan bitwise operator AND (&
) untuk menghapus sedikit.
number &= ~(1UL << n);
Yang jelas n berarti sedikit
nomor. Anda harus membalikkan bit string dengan bitwise operator TIDAK (
~`), maka DAN itu.
XOR operator (^
) dapat digunakan untuk beralih sedikit.
number ^= 1UL << n;
Itu akan beralih n berarti sedikit
nomor`.
Anda tidak't meminta untuk ini, tapi aku mungkin juga menambahkannya.
Untuk memeriksa sedikit, pergeseran jumlah n ke kanan, kemudian bitwise DAN itu:
bit = (number >> n) & 1U;
Yang akan menempatkan nilai n berarti sedikit
nomorke dalam variabel
sedikit`.
Pengaturan n berarti bit
1atau
0` dapat dicapai dengan berikut pada 2's pelengkap C++ pelaksanaan:
number ^= (-x ^ number) & (1UL << n);
Bit n
akan ditetapkan jika x
adalah 1
, dan dibersihkan jika x
adalah 0
. Jika x
memiliki beberapa nilai lainnya, anda mendapatkan sampah. x = !!x
akan booleanize ke 0 atau 1.
Untuk membuat ini independen dari 2's melengkapi negasi perilaku (di mana -1
memiliki semua bit diatur, tidak seperti pada 1's melengkapi atau sign/magnitude C++ implementasi), menggunakan unsigned negasi.
number ^= (-(unsigned long)x ^ number) & (1UL << n);
atau
unsigned long newbit = !!x; // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);
It's umumnya ide yang baik untuk menggunakan unsigned jenis portabel untuk manipulasi bit.
atau
number = (number & ~(1UL << n)) | (x << n);
(jumlah & ~(1UL << n))
akan jelas n'th sedikit dan
(x << n)akan mengatur
n'th sedikit untuk x
.
It's juga umumnya ide yang baik untuk tidak copy/paste kode di umum dan begitu banyak orang menggunakan preprocessor makro (seperti komunitas wiki menjawab lebih jauh ke bawah) atau semacam enkapsulasi.
Menggunakan C++ Standar Library: std::bitset<N>
.
Atau Meningkatkan versi: boost::dynamic_bitset
.
Ada tidak perlu untuk roll anda 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
Meningkatkan versi memungkinkan runtime berukuran bitset dibandingkan dengan standar perpustakaan compile-time berukuran bitset.
Pilihan lainnya adalah dengan menggunakan bit bidang:
struct bits {
unsigned int a:1;
unsigned int b:1;
unsigned int c:1;
};
struct bits mybits;
mendefinisikan 3-bit lapangan (sebenarnya, itu's tiga 1-bit felds). 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 fixed-size bidang bit. Jika tidak, anda harus resor untuk sedikit memutar-mutar teknik yang dijelaskan dalam posting sebelumnya.
Saya menggunakan macro didefinisikan dalam file header untuk menangani bit set dan jelas:
/* 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))
Hal ini kadang-kadang layak menggunakan enum
ke nama bit:
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
Kemudian gunakan nama di kemudian hari. I. e. menulis
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
untuk mengatur, menghapus dan tes. Dengan cara ini anda menyembunyikan angka ajaib dari sisa dari kode anda.
Selain itu saya mendukung Jeremy's solusi.
/*
** 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, let's menganalisis hal-hal...
Ungkapan umum bahwa anda tampaknya memiliki masalah dengan semua ini adalah "(1L << (posn))". Semua ini tidak membuat topeng dengan satu bit pada dan yang akan bekerja dengan tipe integer. "posn" argumen yang menentukan posisi di mana anda ingin sedikit. Jika posn==0, maka ungkapan ini akan mengevaluasi untuk:
0000 0000 0000 0000 0000 0000 0000 0001 binary.
Jika posn==8, maka akan mengevaluasi untuk:
0000 0000 0000 0000 0000 0001 0000 0000 binary.
Dengan kata lain, itu hanya menciptakan lapangan dari 0's-1 di tentukan posisi. Satu-satunya bagian sulit di BitClr() makro di mana kita perlu untuk mengatur tunggal 0 bit dalam bidang 1's. Hal ini dicapai dengan menggunakan 1's melengkapi ekspresi yang sama seperti yang dilambangkan dengan tilde (~) operator.
Setelah masker yang dibuat itu's diterapkan untuk argumen seperti yang anda sarankan, dengan menggunakan bitwise and (&), atau (|), dan xor (^) operator. Sejak topeng adalah tipe lama, macro akan bekerja sama dengan baik di char's, pendek's, int's, atau panjang's.
Intinya adalah bahwa ini adalah sebuah solusi umum untuk seluruh kelas masalah. Itu adalah, tentu saja, mungkin dan bahkan tepat untuk menulis ulang setara dari setiap macro ini dengan eksplisit masker nilai-nilai setiap kali anda perlu satu, tapi mengapa? Ingat, makro substitusi terjadi di preprocessor dan kode yang dihasilkan akan mencerminkan fakta bahwa nilai-nilai dianggap konstan oleh compiler - yaitu's hanya sebagai efisien untuk digunakan umum macro untuk "reinvent the wheel" setiap waktu yang anda butuhkan untuk melakukan manipulasi bit.
Yakin? Berikut ini's beberapa tes kode - saya digunakan Watcom C dengan penuh optimasi dan tanpa menggunakan _cdecl sehingga dihasilkan pembongkaran 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.KELUAR (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 ]-----------------------------------------------------------------
Menggunakan 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'll mungkin bisa bekerja sama dengan representasi HEX, tergantung pada struktur data di mana anda're packing bit.
Untuk pemula saya ingin menjelaskan sedikit lebih banyak dengan sebuah contoh:
Contoh:
value is 0x55;
bitnum : 3rd.
The &
operator ini digunakan check 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)
Toggle atau Flip:
0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)
operator|
: mengatur bit
0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)
Berikut ini's favorit saya sedikit aritmatika makro, yang bekerja untuk setiap tipe unsigned integer array dari unsigned char
hingga size_t
(yang merupakan jenis terbesar yang harus efisien untuk bekerja dengan):
#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.
Karena ini adalah tag "tertanam" aku'll asumsikan anda're menggunakan mikrokontroler. Semua saran di atas berlaku & pekerjaan (read-modify-write, serikat pekerja, struct, dll.).
Namun, saat pertarungan osiloskop berbasis debugging saya kagum untuk menemukan bahwa metode ini memiliki overhead yang cukup besar dalam siklus CPU dibandingkan dengan menulis nilai langsung ke mikro's PORTnSET / PORTnCLEAR register yang membuat perbedaan nyata di mana ada loop ketat / tinggi-frekuensi ISR's toggling pin.
Bagi orang-orang asing: Dalam contoh saya, mikro memiliki seorang jenderal pin-daftar negara PORTn yang mencerminkan pin output, sehingga melakukan PORTn |= BIT_TO_SET hasil di read-modify-write untuk yang mendaftar. Namun, PORTnSET / PORTnCLEAR register mengambil '1' berarti "silakan membuat ini sedikit 1" (SET) atau "silakan membuat ini sedikit nol" (JELAS) dan '0' berarti "meninggalkan pin sendiri". jadi, anda berakhir dengan dua alamat port tergantung apakah anda're pengaturan atau membersihkan bit (tidak selalu nyaman) tapi banyak reaksi lebih cepat dan lebih kecil dirakit kode.
Bitfield pendekatan memiliki keuntungan lain dalam tertanam arena. Anda dapat mendefinisikan struct yang memetakan langsung ke bit di perangkat keras tertentu mendaftar.
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 perlu menyadari dari sedikit packing order - saya pikir itu's MSB pertama, tapi ini mungkin implementation-dependent. Juga, memverifikasi bagaimana anda compiler penangan bidang penyeberangan byte batas.
Anda kemudian dapat membaca, menulis, menguji nilai-nilai individu seperti sebelumnya.
#define bit_test(x, y) ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )
Contoh penggunaan:
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 untuk menjadi cepat (mengingat fleksibilitas) dan non-bercabang. Itu hasil yang efisien SPARC kode mesin ketika disusun Sun Studio 8; I've juga diuji menggunakan MSVC++ 2008 pada amd64. It's mungkin untuk membuat yang serupa macro untuk pengaturan dan penyelenggaraan kliring bit. Perbedaan utama dari solusi ini dibandingkan dengan banyak orang lain di sini adalah bahwa ia bekerja untuk setiap lokasi di hampir semua jenis variabel.
Lebih umum, untuk sewenang-wenang berukuran bitmap:
#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)))
Program ini adalah untuk mengubah data bit dari 0 ke 1 atau 1 ke 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're banyak melakukan sedikit memutar-mutar anda mungkin ingin menggunakan masker yang akan membuat semuanya lebih cepat. Berikut fungsi yang sangat cepat dan masih fleksibel (mereka memungkinkan sedikit memutar-mutar di bit peta 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' di 16 bit integer yang anda lakukan berikut ini:
TSetBit( n, &my_int);
It's terserah anda untuk memastikan bahwa jumlah bit adalah dalam kisaran yang sedikit peta yang anda lulus. Perhatikan bahwa untuk little endian prosesor yang byte, kata, perkataan, qwords, dll., peta dengan benar untuk satu sama lain dalam memori (alasan utama bahwa little endian prosesor 'baik' dari big-endian prosesor, ah, aku merasa api perang yang akan datang di...).
Jika anda ingin melakukan ini semua operasi dengan pemrograman C di Linux kernel maka saya sarankan untuk menggunakan standar Api 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 yang terjadi dalam satu langkah. Jadi ini semua dijamin akan atom bahkan di SMP dan komputer yang berguna untuk menjaga koherensi di prosesor.
Memperluas pada bitset
menjawab:
#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;
}
Visual C 2010, dan mungkin banyak kompiler lain, memiliki dukungan langsung untuk operasi bit yang dibangun di. Anehnya, ini bekerja, 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
membuat pengaturan dan penyelenggaraan kliring bools mudah.
Untuk menemukan karakter yang tidak dapat 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;
}
}
Catatan tidak ada "khusus" tentang kode ini. Memperlakukan sedikit seperti integer yang secara teknis, itu adalah. 1 bit integer yang dapat menampung nilai-nilai 2, dan 2 nilai saja.
Saya pernah menggunakan pendekatan ini untuk menemukan duplikat catatan kredit, di mana loan_number adalah ISAM kunci, menggunakan 6 digit pinjaman angka sebagai indeks ke dalam bit array. Kejam cepat, dan setelah 8 bulan, membuktikan bahwa sistem mainframe kami mendapatkan data-data dari dalam kenyataannya rusak. Kesederhanaan bit array membuat kepercayaan dalam kebenaran mereka sangat tinggi dibandingkan dengan mencari pendekatan misalnya.