To ustawi nth th number. npowinno wynosić zero, jeśli chcesz ustawić 1bit st i tak dalej n-1, jeśli chcesz ustawić nbit.
Użyj 1ULLjeśli numberjest szerszy niż unsigned long; promocja 1UL << nnie następuje dopiero po sprawdzeniu, 1UL << ngdzie zachowanie jest niezdefiniowane, aby przesunąć się o więcej niż szerokość a long. To samo dotyczy wszystkich pozostałych przykładów.
Trochę się wyczyściłem
Użyj bitowego operatora AND ( &), aby trochę wyczyścić.
number &=~(1UL<< n);
To wyczyści ntrochę number. Musisz odwrócić ciąg bitów za pomocą bitowego operatora NOT ( ~), a następnie AND.
Trochę się przełączam
Za pomocą operatora XOR ( ^) można nieco przełączyć.
number ^=1UL<< n;
To przełączy ntrochę number.
Trochę sprawdzam
Nie prosiłeś o to, ale równie dobrze mogę to dodać.
Aby to sprawdzić, przesuń liczbę n w prawo, a następnie bitowo ORAZ:
bit =(number >> n)&1U;
Spowoduje to wstawienie wartości ntego bitu numberdo zmiennej bit.
Zmiana n- tego bitu na x
Ustawienie ntego bitu na jeden 1lub jeden 0może być osiągnięte za pomocą następujących elementów w implementacji C ++ dla komplementu 2:
number ^=(-x ^ number)&(1UL<< n);
Bit nzostanie ustawiony, jeśli xjest 1, i wyczyszczony, jeśli xjest 0. Jeśli xma jakąś inną wartość, dostajesz śmieci. x = !!xwstawi wartość booleanize na 0 lub 1.
Aby uniezależnić to od zachowania negacji dopełniacza 2 (gdzie -1ustawiono wszystkie bity, w przeciwieństwie do implementacji dopełniacza 1 lub implementacji C ++ znaku / wielkości), użyj negacji bez znaku.
number ^=(-(unsignedlong)x ^ number)&(1UL<< n);
lub
unsignedlong newbit =!!x;// Also booleanize to force 0 or 1
number ^=(-newbit ^ number)&(1UL<< n);
Zasadniczo dobrym pomysłem jest używanie typów niepodpisanych do przenośnej manipulacji bitami.
lub
number =(number &~(1UL<< n))|(x << n);
(number & ~(1UL << n))wyczyści nbit th i (x << n)ustawi nth th na x.
Dobrym pomysłem jest również, aby nie kopiować / wklejać kodu, więc wiele osób korzysta z makr preprocesora (np . Odpowiedzi społeczności wiki na dole ) lub jakiegoś rodzaju enkapsulacji.
Chciałbym zauważyć, że na platformach, które mają natywną obsługę bit set / clear (np. Mikrokontrolery AVR), kompilatory często tłumaczą „myByte | = (1 << x)” na natywne instrukcje bit set / clear za każdym razem, gdy x stała, np .: (1 << 5), lub const unsigned x = 5.
Aaron
52
bit = liczba & (1 << x); nie wstawi wartości bitu x do bitu, chyba że bit ma typ _Bool (<stdbool.h>). W przeciwnym razie bit = !! (liczba & (1 << x)); będzie ..
Chris Young
23
dlaczego nie zmienisz ostatniego nabit = (number >> x) & 1
aaronman
42
1jest intliterałem, który jest podpisany. Więc wszystkie operacje tutaj działają na podpisanych numerach, co nie jest dobrze zdefiniowane przez standardy. Standardy nie gwarantują uzupełnienia dwóch lub przesunięcia arytmetycznego, więc lepiej jest użyć 1U.
Siyuan Ren,
50
Wolę number = number & ~(1 << n) | (x << n);zmienić n-ty bit na x.
jiasli
459
Korzystanie z standardowa biblioteka C ++: std::bitset<N>.
+1. Nie to, że zestaw std :: bitset nadaje się do użycia z „C”, ale ponieważ autor oznaczył swoje pytanie „C ++”, AFAIK, twoja odpowiedź jest najlepsza tutaj ... std :: vector <bool> to inny sposób, jeśli ktoś zna jego zalety i zalety
paercebal 19.09.08
23
@andrewdotnich: vector <bool> jest (niestety) specjalizacją, która przechowuje wartości jako bity. Aby uzyskać więcej informacji, zobacz gotw.ca/publications/mill09.htm ...
Niklas,
71
Może nikt o tym nie wspomniał, ponieważ oznaczono to jako osadzone. W większości systemów wbudowanych unikasz STL jak zarazy. Obsługa wspomagania jest prawdopodobnie bardzo rzadkim ptakiem, który można spotkać wśród większości wbudowanych kompilatorów.
Lundin,
17
@Martin To bardzo prawda. Poza konkretnymi zabójcami wydajności, takimi jak STL i szablony, wiele systemów osadzonych nawet całkowicie omija całe standardowe biblioteki, ponieważ weryfikacja ich jest bardzo trudna. Większość osadzonych gałęzi obejmuje standardy takie jak MISRA, które wymagają narzędzi do analizy kodu statycznego (każdy profesjonalista oprogramowania powinien używać takich narzędzi btw, a nie tylko ludzi osadzonych). Zasadniczo ludzie mają lepsze rzeczy do roboty niż przeprowadzanie analizy statycznej w całej standardowej bibliotece - jeśli jej kod źródłowy jest dla nich nawet dostępny w określonym kompilatorze.
Lundin,
37
@Lundin: Twoje wypowiedzi są zbyt szerokie (dlatego nie warto się kłócić). Jestem pewien, że potrafię znaleźć sytuacje, w których są prawdziwe. To nie zmienia mojego początkowego punktu. Obie te klasy doskonale nadają się do użycia w systemach wbudowanych (i wiem na pewno, że są używane). Twoja początkowa uwaga na temat nieużywania STL / Boost w systemach wbudowanych jest również błędna. Jestem pewien, że istnieją systemy, które ich nie używają, a nawet systemy, które ich używają, są używane rozsądnie, ale stwierdzenie, że nie są używane, jest po prostu nieprawidłowe (ponieważ istnieją systemy, w których były używane).
definiuje pole 3-bitowe (w rzeczywistości są to trzy 1-bitowe pola). Operacje bitowe stają się teraz nieco (haha) prostsze:
Aby ustawić lub wyczyścić nieco:
mybits.b =1;
mybits.c =0;
Aby nieco przełączyć:
mybits.a =!mybits.a;
mybits.b =~mybits.b;
mybits.c ^=1;/* all work */
Trochę sprawdzam:
if(mybits.c)//if mybits.c is non zero the next line below will execute
Działa to tylko z polami bitowymi o stałej wielkości. W przeciwnym razie musisz uciekać się do technik kruszenia bitów opisanych w poprzednich postach.
Zawsze uważałem, że używanie pól bitowych to zły pomysł. Nie masz kontroli nad kolejnością przydzielania bitów (od góry lub od dołu), co uniemożliwia serializację wartości w sposób stabilny / przenośny, z wyjątkiem bit-at-a-time. Niemożliwe jest również mieszanie arytmetyki bitów DIY z polami bitowymi, na przykład tworzenie maski, która testuje kilka bitów jednocześnie. Możesz oczywiście użyć && i mam nadzieję, że kompilator zoptymalizuje go poprawnie ...
R .. GitHub ZATRZYMAJ LODĘ
34
Pola bitowe są złe na tak wiele sposobów, że prawie mógłbym napisać o tym książkę. W rzeczywistości prawie musiałem to zrobić w przypadku programu terenowego, który wymagał zgodności z MISRA-C. MISRA-C wymusza udokumentowanie wszystkich zachowań zdefiniowanych w implementacji, więc skończyłem pisać sporo eseju na temat wszystkiego, co może pójść nie tak w polach bitowych. Kolejność bitów, endianowość, bity dopełniania, bajty dopełniania, różne inne problemy z wyrównaniem, niejawne i jawne konwersje typów do i z pola bitowego, UB, jeśli int nie jest używane i tak dalej. Zamiast tego używaj operatorów bitowych, aby zmniejszyć liczbę błędów i przenośnego kodu. Pola bitowe są całkowicie zbędne.
Lundin,
44
Podobnie jak większość funkcji językowych, pola bitowe mogą być używane poprawnie lub mogą być nadużywane. Jeśli potrzebujesz spakować kilka małych wartości w jedną liczbę całkowitą, pola bitowe mogą być bardzo przydatne. Z drugiej strony, jeśli zaczniesz zakładać, w jaki sposób pola bitów są odwzorowywane na rzeczywiste zawierające int, po prostu pytasz o kłopoty.
Ferruccio,
4
@endolith: To nie byłby dobry pomysł. Możesz sprawić, że będzie działał, ale niekoniecznie będzie przenośny na inny procesor, inny kompilator lub nawet na następną wersję tego samego kompilatora.
Ferruccio,
3
@Yasky i Ferruccio, otrzymując różne odpowiedzi na sizeof () dla tego podejścia, powinny zilustrować problemy ze zgodnością nie tylko między kompilatorami, ale i sprzętem. Czasami oszukujemy się, że rozwiązaliśmy te problemy z językami lub zdefiniowanymi środowiskami uruchomieniowymi, ale tak naprawdę sprowadza się to do „czy będzie działać na moim komputerze?”. Osadziliście facetów, mam mój szacunek (i sympatie).
Kelly S. French
181
Używam makr zdefiniowanych w pliku nagłówkowym do obsługi zestawu bitów i czyszczenia:
/* 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))
Uh, zdaję sobie sprawę, że to jest 5-letni post, ale nie ma duplikacji argumentów w żadnym z tych makr, Dan
Robert Kelly
11
BITMASK_CHECK(x,y) ((x) & (y))musi być ((x) & (y)) == (y)inaczej zwróci niepoprawny wynik na masce wielobitowej (np. 5vs. 3) / * Witam wszystkich grabarzy
:)
7
1powinien być (uintmax_t)1podobny w przypadku, gdy ktoś spróbuje użyć tych makr na longtypie większym lub większym
Zamiast tego możesz utworzyć clearbits()funkcję zamiast &= ~. Dlaczego używasz do tego wyliczenia? Myślałem, że służą one do tworzenia wiązki unikalnych zmiennych z ukrytą dowolną wartością, ale przypisujesz każdemu z nich określoną wartość. Jaka jest więc korzyść w porównaniu do definiowania ich jako zmiennych?
endolith,
4
@endolith: Zastosowanie enums do zestawów stałych pokrewnych ma długą tradycję w programowaniu c. Podejrzewam, że dzięki nowoczesnym kompilatorom jedyną przewagą nad nimi const shortjest to, że są one wyraźnie zgrupowane. A kiedy chcesz ich do czegoś innego niż maski bitowe, otrzymujesz automatyczną numerację. Oczywiście w c ++ tworzą one również różne typy, co daje trochę dodatkowych statycznych kontroli błędów.
dmckee --- były moderator kociak
Wejdziesz w niezdefiniowane stałe wyliczeniowe, jeśli nie zdefiniujesz stałej dla każdej z możliwych wartości bitów. Jaka jest enum ThingFlagswartość ThingError|ThingFlag1, na przykład?
Luis Colorado
6
Jeśli używasz tej metody, pamiętaj, że stałe wyliczeniowe są zawsze typu podpisanego int. Może to powodować różnego rodzaju subtelne błędy z powodu niejawnej promocji liczb całkowitych lub operacji bitowych na podpisanych typach. thingstate = ThingFlag1 >> 1wywoła na przykład zachowanie zdefiniowane w implementacji. thingstate = (ThingFlag1 >> x) << ymoże wywoływać niezdefiniowane zachowanie. I tak dalej. Aby być bezpiecznym, zawsze przesyłaj do niepodpisanego typu.
Lundin
1
@Lundin: Od C ++ 11 możesz ustawić podstawowy typ wyliczenia, np .: enum My16Bits: unsigned short { ... };
/*
** Bit set, clear, and test operations
**
** public domain snippet by Bob Stout
*/typedefenum{ERROR =-1, FALSE, TRUE} LOGICAL;#define BOOL(x)(!(!(x)))#defineBitSet(arg,posn)((arg)|(1L<<(posn)))#defineBitClr(arg,posn)((arg)&~(1L<<(posn)))#defineBitTst(arg,posn) BOOL((arg)&(1L<<(posn)))#defineBitFlp(arg,posn)((arg)^(1L<<(posn)))
OK, przeanalizujmy rzeczy ...
Powszechnym wyrażeniem, z którym wydaje się, że masz problemy we wszystkich tych przypadkach, jest „(1L << (posn))”. Wszystko to polega na utworzeniu maski z jednym bitem, która będzie działać z dowolnym typem całkowitym. Argument „posn” określa pozycję, w której chcesz bit. Jeśli posn == 0, to wyrażenie będzie miało wartość:
00000000000000000000000000000001 binary.
Jeśli posn == 8, oceni, że:
00000000000000000000000100000000 binary.
Innymi słowy, po prostu tworzy pole zer z 1 w określonej pozycji. Jedyną trudną częścią jest makro BitClr (), w którym musimy ustawić pojedynczy bit 0 w polu 1. Dokonuje się tego za pomocą dopełniacza 1 tego samego wyrażenia, co oznaczono przez operator tyldy (~).
Po utworzeniu maski jest ona stosowana do argumentu tak, jak sugerujesz, za pomocą operatorów bitowych i (&) lub (|) i xor (^). Ponieważ maska jest długa, makra będą działać równie dobrze na char, short, int lub long.
Najważniejsze jest to, że jest to ogólne rozwiązanie całej klasy problemów. Oczywiście jest możliwe, a nawet właściwe przepisać odpowiednik któregokolwiek z tych makr za pomocą jawnych wartości maski za każdym razem, gdy jest to potrzebne, ale dlaczego? Pamiętaj, że zastępowanie makr zachodzi w preprocesorze, więc wygenerowany kod będzie odzwierciedlał fakt, że kompilator uważa, że wartości są stałe - tzn. Użycie makr uogólnionych jest równie skuteczne, jak „ponowne wymyślenie koła” za każdym razem, gdy trzeba zrobić trochę manipulacji.
Nie przekonany? Oto kod testowy - użyłem Watcom C z pełną optymalizacją i bez użycia _cdecl, aby wynikowy demontaż był jak najbardziej czysty:
#define BOOL(x)(!(!(x)))#defineBitSet(arg,posn)((arg)|(1L<<(posn)))#defineBitClr(arg,posn)((arg)&~(1L<<(posn)))#defineBitTst(arg,posn) BOOL((arg)&(1L<<(posn)))#defineBitFlp(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;}
2 rzeczy na ten temat: (1) w czytaniu makr niektórzy mogą błędnie sądzić, że makra faktycznie ustawiają / usuwają / odwracają bity w arg, jednak nie ma przypisania; (2) twój test.c nie jest kompletny; Podejrzewam, że jeśli prowadziłbyś więcej spraw, znalazłbyś problem (ćwiczenie dla czytelników)
Dan
19
-1 To jest po prostu dziwne zaciemnienie. Nigdy nie wymyślaj na nowo języka C, ukrywając składnię języka za makrami, jest to bardzo zła praktyka. Potem kilka dziwactw: po pierwsze, 1L jest podpisany, co oznacza, że wszystkie operacje bitowe będą wykonywane na podpisanym typie. Wszystko przekazane do tych makr zostanie zwrócone jako podpisane długo. Niedobrze. Po drugie, będzie to działać bardzo nieefektywnie na mniejszych procesorach, ponieważ wymusza to długo, gdy operacje mogły być na poziomie int. Po trzecie, podobne do funkcji makra są źródłem wszelkiego zła: nie masz żadnego rodzaju bezpieczeństwa. Również poprzedni komentarz na temat braku przypisania jest bardzo ważny.
Lundin,
2
To się nie powiedzie, jeśli tak argjest long long. 1Lmusi być najszerszym możliwym typem, więc (uintmax_t)1. (Możesz uciec 1ull)
MM
Czy zoptymalizowałeś rozmiar kodu? Na głównych procesorach Intel otrzymujesz częściowe rejestrowanie przeciągnięć podczas odczytu AX lub EAX po powrocie tej funkcji, ponieważ zapisuje 8-bitowe komponenty EAX. (Jest w porządku na procesorach AMD lub innych, które nie zmieniają nazw rejestrów częściowych oddzielnie od pełnego rejestru. Haswell / Skylake nie zmieniają nazw AL oddzielnie, ale zmieniają nazwy AH. ).
Peter Cordes,
37
Użyj operatorów bitowych: &|
Aby ustawić ostatni bit w 000b:
foo = foo |001b
Aby sprawdzić ostatni bit w foo:
if( foo &001b)....
Aby wyczyścić ostatni bit w foo:
foo = foo &110b
Użyłem XXXbdla jasności. Prawdopodobnie będziesz pracować z reprezentacją HEX, w zależności od struktury danych, w której pakujesz bity.
Oto moje ulubione bitowe makro arytmetyczne, które działa na każdym typie tablicy liczb całkowitych bez znaku od unsigned chardo size_t(który jest największym typem, który powinien być wydajny w pracy):
#define BITOP(a,b,op) \
((a)[(size_t)(b)/(8*sizeof*(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof*(a)))))
Dobrze jest czytać, ale należy pamiętać o możliwych skutkach ubocznych. Korzystanie BITOP(array, bit++, |=);z pętli najprawdopodobniej nie spełni oczekiwań osoby dzwoniącej.
zapomniałem
W rzeczy samej. =) Jeden wariant może wolisz jest oddzielenie go na 2 makra 1 do adresowania element tablicy, a druga dla przesunięcia bitu w miejscu, ala BITCELL(a,b) |= BITMASK(a,b);(zarówno odbioru ajako argument do określenia wielkości, ale ten nigdy nie oceniać a, ponieważ pojawia się tylko w sizeof).
R .. GitHub ZATRZYMAJ LÓD
1
@R .. Ta odpowiedź jest naprawdę stara, ale w tym przypadku prawdopodobnie wolałbym funkcję niż makro.
PC Luddite,
Minor: 3rd (size_t)obsada wydaje się być nie tylko, aby zapewnić pewne niepodpisane matematyki z %. Może (unsigned)tam
chux - Przywróć Monikę
(size_t)(b)/(8*sizeof *(a))Niepotrzebnie może zmniejszyć bprzed podziałem. Tylko problem z bardzo dużymi tablicami bitów. Wciąż ciekawe makro.
chux - Przywróć Monikę
25
Ponieważ jest to oznaczone jako „osadzone”, założę, że używasz mikrokontrolera. Wszystkie powyższe sugestie są prawidłowe i działają (odczyt-modyfikacja-zapis, związki, struktury itp.).
Jednak podczas serii debugowania opartego na oscyloskopie byłem zaskoczony, gdy zauważyłem, że metody te mają znaczny narzut w cyklach procesora w porównaniu z zapisywaniem wartości bezpośrednio w rejestrach PORTnSET / PORTnCLEAR mikroprocesora, co stanowi istotną różnicę w przypadku ciasnych pętli / wysokich Częstotliwość przełączania pinów ISR.
Dla tych, którzy nie są zaznajomieni: W moim przykładzie mikro ma ogólny rejestr stanu PORTn stanu pinów, który odzwierciedla styki wyjściowe, więc wykonanie PORTn | = BIT_TO_SET powoduje odczyt i modyfikację zapisu w tym rejestrze. Jednak rejestry PORTnSET / PORTnCLEAR przyjmują „1”, co oznacza „proszę zrobić ten bit 1” (SET) lub „proszę zrobić ten bit zero” (CLEAR) i „0”, co oznacza „zostawić pin w spokoju”. tak więc otrzymujesz dwa adresy portów w zależności od tego, czy ustawiasz, czy kasujesz bit (nie zawsze jest to wygodne), ale znacznie szybszą reakcję i mniejszy złożony kod.
Micro był Coldfire MCF52259, używając C w Codewarrior. Patrzenie na deasembler / asm jest przydatnym ćwiczeniem, ponieważ pokazuje wszystkie kroki, przez które CPU musi przejść, aby wykonać nawet najbardziej podstawową operację. <br> Zauważyliśmy także inne instrukcje zatrzymywania procesora w pętlach krytycznych czasowo - ograniczanie zmiennej przez wykonanie var% = max_val kosztuje stos cykli procesora za każdym razem, podczas gdy (var> max_val) var- = max_val używa tylko kilka instrukcji. <br> Dobry przewodnik po kilku innych sztuczkach znajduje się tutaj: codeproject.com/Articles/6154/...
John U
Co jeszcze ważniejsze, rejestry we / wy mapowane w pamięci pomocniczej zapewniają mechanizm aktualizacji atomowych. Odczyt / modyfikacja / zapis może pójść bardzo źle, jeśli sekwencja zostanie przerwana.
Ben Voigt
1
Należy pamiętać, że wszystkie rejestry portów zostaną zdefiniowane jako, volatilea zatem kompilator nie jest w stanie wykonać żadnych optymalizacji kodu obejmującego takie rejestry. Dlatego dobrą praktyką jest dezasemblowanie takiego kodu i sprawdzanie, jak wyszło na poziomie asemblera.
Lundin
24
Podejście do pola bitowego ma inne zalety na wbudowanej arenie. Możesz zdefiniować strukturę, która będzie mapowana bezpośrednio na bity w konkretnym rejestrze sprzętowym.
structHwRegister{unsignedint errorFlag:1;// one-bit flag fieldunsignedintMode:3;// three-bit mode fieldunsignedintStatusCode:4;// four-bit status code};structHwRegister CR3342_AReg;
Musisz być świadomy kolejności pakowania bitów - myślę, że to najpierw MSB, ale może to zależeć od implementacji. Sprawdź także, w jaki sposób procedury obsługi kompilatora przekraczają granice bajtów.
Następnie możesz czytać, pisać, testować poszczególne wartości jak poprzednio.
Prawie wszystko o polach bitowych jest zdefiniowane w implementacji. Nawet jeśli uda ci się znaleźć wszystkie szczegóły dotyczące tego, jak konkretny kompilator je implementuje, użycie ich w kodzie z pewnością sprawi, że nie będzie przenośny.
Lundin,
1
@Lundin - To prawda, ale wbudowane bity systemowe (szczególnie w rejestrach sprzętowych, do których odnosi się moja odpowiedź) nigdy nie będą przydatne.
Roddy
1
Być może nie między całkowicie różnymi procesorami. Ale najprawdopodobniej chcesz, aby był przenośny między kompilatorami i między różnymi projektami. I jest wiele wbudowanych „bitów”, które w ogóle nie są związane ze sprzętem, takich jak kodowanie / dekodowanie protokołu danych.
Lundin,
... a jeśli nauczysz się używać pól bitowych do programowania wbudowanego, zauważysz, że Twój kod X86 działa szybciej i jest szczuplejszy. Nie w prostych testach porównawczych, w których masz całą maszynę do zniszczenia testów, ale w rzeczywistych środowiskach wielozadaniowych, w których programy konkurują o zasoby. Zaleta CISC - którego pierwotnym celem projektowym było nadrobienie procesorów szybszych niż magistrale i wolna pamięć.
20
Sprawdź trochę w dowolnym miejscu w zmiennej dowolnego typu:
int main(void){unsignedchar 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));return0;}
Uwagi:
Został zaprojektowany jako szybki (biorąc pod uwagę jego elastyczność) i nierozgałęziony. Skutkuje to wydajnym kodem maszynowym SPARC podczas kompilacji Sun Studio 8; Przetestowałem to również przy użyciu MSVC ++ 2008 na amd64. Możliwe jest tworzenie podobnych makr do ustawiania i czyszczenia bitów. Kluczową różnicą tego rozwiązania w porównaniu z wieloma innymi tutaj jest to, że działa on dla dowolnej lokalizacji w prawie każdym typie zmiennej.
Jeśli robisz dużo kręcenia, możesz chcieć użyć masek, które przyspieszą. Poniższe funkcje są bardzo szybkie i wciąż elastyczne (pozwalają na kręcenie bitów w mapach bitowych o dowolnym rozmiarze).
constunsignedcharTQuickByteMask[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.
*/voidTSetBit(short bit,unsignedchar*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.
*/voidTResetBit(short bit,unsignedchar*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.
*/voidTToggleBit(short bit,unsignedchar*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.
*/shortTIsBitSet(short bit,constunsignedchar*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])return1;return0;}/** Checks specified bit.
*
* @return 1 if bit reset else 0.
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/shortTIsBitReset(short bit,constunsignedchar*bitmap){returnTIsBitSet(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).
*/intTCountBits(constunsignedchar*bitmap,int size){int i, count =0;for(i=0; i<size; i++)if(TIsBitSet(i, bitmap))
count++;return count;}
Uwaga: aby ustawić bit „n” w 16-bitowej liczbie całkowitej, wykonaj następujące czynności:
TSetBit( n,&my_int);
Od Ciebie zależy, czy liczba bitów będzie w zakresie przekazywanej mapy bitów. Zwróć uwagę, że w przypadku małych procesorów endian, które bajty, słowa, dwory, qwords itp. Poprawnie odwzorowują się w pamięci (główny powód, dla którego małe procesory endianowe są „lepsze” niż procesory big-endian, ach, czuję, że nadchodzi wojna płomieni na...).
Nie używaj tabeli dla funkcji, którą można zaimplementować za pomocą jednego operatora. TQuickByteMask [n] jest równoważne (1 << n). Również krótkie spieranie argumentów to bardzo zły pomysł. / I% faktycznie będzie dzieleniem, a nie bitową zmianą / bitową, a ponieważ znak dzielony przez potęgę 2 nie może być zaimplementowany bitowo. Powinieneś zrobić argument typu unsigned int!
R .. GitHub ZATRZYMAJ LÓD
Po co to ma sens? To tylko sprawia, że kod jest wolniejszy i trudniejszy do odczytania? Nie widzę w tym ani jednej przewagi. 1u << n jest łatwiejszy do odczytania dla programistów C i, miejmy nadzieję, może zostać przetłumaczony na instrukcję CPU pojedynczego zegara. Z drugiej strony twój podział zostanie przetłumaczony na około 10 tyknięć, a nawet tak źle, jak do 100 tyknięć, w zależności od tego, jak słabo konkretna architektura obsługuje podział. Jeśli chodzi o funkcję bitmapy, sensowniej byłoby mieć tablicę odnośników tłumaczącą każdy indeks bitowy na indeks bajtowy, aby zoptymalizować szybkość.
Lundin,
2
Jeśli chodzi o big / little endian, big endian będzie mapował liczby całkowite i surowe dane (na przykład łańcuchy) w ten sam sposób: od lewej do prawej msb do lsb na całej mapie bitowej. Podczas gdy mały Endian odwzorowuje liczby całkowite od lewej do prawej jako 7-0, 15-8, 23-18, 31-24, ale nieprzetworzone dane są wciąż od lewej do prawej msb do lsb. Więc jak mało endian jest lepszy dla twojego konkretnego algorytmu jest całkowicie poza mną, wydaje się być odwrotnie.
Lundin,
2
@R .. Tabela może być przydatna, jeśli twoja platforma nie może się efektywnie przesuwać, tak jak stare mikroczipy mcu, ale oczywiście wtedy podział w próbce jest absolutnie nieefektywny
jeb
12
Użyj tego:
intToggleNthBit(unsignedchar n,int num ){if(num &(1<< n))
num &=~(1<< n);else
num |=(1<< n);return num;}
set_bit Atomicallyset 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 andreturn its old value
test_and_clear_bit Clear a bit andreturn its old value
test_and_change_bit Change a bit andreturn its old value
test_bit Determine whether a bit isset
Uwaga: Tutaj cała operacja odbywa się w jednym kroku. Gwarantuje to, że są one atomowe nawet na komputerach SMP i są przydatne do zachowania spójności między procesorami.
Visual C 2010 i być może wiele innych kompilatorów ma wbudowane bezpośrednie wsparcie dla operacji boolowskich. Trochę ma dwie możliwe wartości, podobnie jak boolean, więc zamiast tego możemy używać boolanów - nawet jeśli zajmują więcej miejsca niż jeden bit w pamięć w tej reprezentacji. To działa, nawet sizeof()operator działa poprawnie.
Więc na twoje pytanie IsGph[i] =1, lubIsGph[i] =0 ułatw sobie ustawianie i usuwanie booli.
Aby znaleźć niedrukowalne znaki:
// 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;}}
Uwaga: w tym kodzie nie ma nic „specjalnego”. Traktuje to trochę jak liczbę całkowitą - co technicznie jest. 1-bitowa liczba całkowita, która może pomieścić 2 wartości i tylko 2 wartości.
Kiedyś użyłem tego podejścia, aby znaleźć duplikaty rekordów pożyczek, gdzie numer_ pożyczki był kluczem ISAM, używając 6-cyfrowego numeru pożyczki jako indeksu w tablicy bitów. Niesamowicie szybki i po 8 miesiącach udowodnił, że system mainframe, z którego otrzymywaliśmy dane, w rzeczywistości działał nieprawidłowo. Prostota tablic bitowych sprawia, że pewność ich poprawności jest bardzo wysoka - na przykład w stosunku do wyszukiwania.
zestaw std :: bitset jest rzeczywiście zaimplementowany jako bit przez większość kompilatorów
galinette
@ Galinette, uzgodnione. Plik nagłówkowy #include <bitset> jest dobrym zasobem pod tym względem. Również wektor specjalnej klasy <bool> na potrzeby zmiany rozmiaru wektora. C ++ STL, wydanie drugie, Nicolai M. Josuttis omawia je wyczerpująco odpowiednio na stronach 650 i 281. C ++ 11 dodaje kilka nowych możliwości do std :: bitset, szczególnie interesuje mnie funkcja skrótu w nieuporządkowanych kontenerach. Dzięki za heads-up! Skasuję mój komentarz dotyczący skurczu mózgu. Już dość śmieci w Internecie. Nie chcę tego dodawać.
3
To wykorzystuje co najmniej cały bajt pamięci dla każdego bool. Może nawet 4 bajty dla konfiguracji C89, które używają intdo implementacjibool
MM
@MattMcNabb, masz rację. W C ++ rozmiar typu int niezbędny do implementacji wartości logicznej nie jest określony przez standard. Zdałem sobie sprawę, że ta odpowiedź była kiedyś błędna, ale postanowiłem ją tutaj zostawić, ponieważ ludzie najwyraźniej uważają ją za przydatną. Dla tych, którzy chcą użyć bitów, komentarz Galinette jest najbardziej pomocny, podobnie jak moja biblioteka bitów tutaj ... stackoverflow.com/a/16534995/1899861
2
@RocketRoy: Prawdopodobnie warto zmienić zdanie, które twierdzi, że jest to przykład „operacji bitowych”.
Załóżmy, że najpierw kilka num = 55 liczb całkowitych wykonuje operacje bitowe (ustaw, pobierz, wyczyść, przełącz). n = 40 bitowa pozycja do wykonywania operacji bitowych.
Jak się trochę dostać?
Aby uzyskać nthodrobinę num prawej zmiany num, nrazy. Następnie wykonaj bitowe AND &z 1.
Aby nieco przełączyć, używamy bitowego XOR ^ operatora . Bitowy operator XOR ocenia na 1, jeśli odpowiadający mu bit obu argumentów jest inny, w przeciwnym razie ocenia na 0.
Co oznacza, że należy trochę przełączyć, musimy wykonać operację XOR z bitem, który chcesz przełączyć i 1.
num ^=(1<< n);// Equivalent to; num = num ^ (1 << n);
Jak to działa?
Jeśli bit do przełączenia wynosi 0, wówczas 0 ^ 1 => 1 .
Jeżeli bit jest 1, aby przełączyć wtedy 1 ^ 1 => 0.
Dziękuję za szczegółowe wyjaśnienie. Oto link do problemu ćwiczeniowego dla BIT Magic link
Chandra Shekhar
4
Jak ustawić, wyczyścić i przełączyć jeden bit?
Aby rozwiązać problem typowego pułapki kodowania podczas próby utworzenia maski: 1nie zawsze jest wystarczająco szeroki
Jakie problemy się zdarzają, gdy numberjest szerszy niż 1? xmoże być zbyt wielki dla zmiany 1 << xprowadzącej do niezdefiniowanego zachowania (UB). Nawet jeśli xnie jest zbyt wielki, ~może nie odwrócić wystarczającej liczby najbardziej znaczących bitów.
// assume 32 bit int/unsignedunsignedlonglong 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
Aby zapewnić, że 1 jest wystarczająco szeroki:
Kod może użyć 1ulllub pedantycznie (uintmax_t)1i pozwolić kompilatorowi zoptymalizować.
number |=(1ull<< x);
number |=((uintmax_t)1<< x);
Lub obsada - co powoduje problemy z kodowaniem / przeglądem / konserwacją, dzięki czemu obsada jest poprawna i aktualna.
number |=(type_of_number)1<< x;
Lub delikatnie promuj 1poprzez wymuszanie operacji matematycznej, która jest co najmniej tak szeroka jak rodzaj number.
number |=(number*0+1)<< x;
Jak w większości bitowe manipulacji, najlepiej do pracy z niepodpisanych typów zamiast podpisanych te
Ciekawe spojrzenie na stare pytanie! Ani number |= (type_of_number)1 << x;nie number |= (number*0 + 1) << x;należy ustawiać bitu znaku typu podpisanego ... Właściwie nie jest number |= (1ull << x);. Czy istnieje przenośny sposób na zrobienie tego według pozycji?
chqrlie
1
@chqrlie IMO, najlepszym sposobem na uniknięcie ustawienia bitu znakowego i ryzykowania UB lub IDB ze zmianami jest użycie typów niepodpisanych . Wysoce przenośny przesunięcie podpisany kod jest zbyt zawiłe do przyjęcia.
chux - Przywróć Monikę
3
Wersja szablonowa C ++ 11 (umieszczona w nagłówku):
Ten kod jest uszkodzony. (Poza tym, dlaczego macie ;definicje funkcji?)
melpomene
@melpomene Kod nie jest uszkodzony, przetestowałem go. Czy masz na myśli, że się nie skompiluje lub że wynik jest nieprawidłowy? Informacje o dodatkowym ';' Nie pamiętam, że można je rzeczywiście usunąć.
Joakim L. Christiansen
(variable & bits == bits)?
melpomene
Dziękujemy za zwrócenie uwagi, że miało to być((variable & bits) == bits)
Joakim L. Christiansen
użyj std::bitsetw c ++ 11
pqnet
0
Ten program jest oparty na powyższym rozwiązaniu @ Jeremy. Jeśli ktoś chce szybko się pobawić.
Odpowiedzi:
Ustawienie trochę
Użyj bitowego operatora OR (
|
), aby ustawić bit.To ustawi
n
th thnumber
.n
powinno wynosić zero, jeśli chcesz ustawić1
bit st i tak dalejn-1
, jeśli chcesz ustawićn
bit.Użyj
1ULL
jeślinumber
jest szerszy niżunsigned long
; promocja1UL << n
nie następuje dopiero po sprawdzeniu,1UL << n
gdzie zachowanie jest niezdefiniowane, aby przesunąć się o więcej niż szerokość along
. To samo dotyczy wszystkich pozostałych przykładów.Trochę się wyczyściłem
Użyj bitowego operatora AND (
&
), aby trochę wyczyścić.To wyczyści
n
trochęnumber
. Musisz odwrócić ciąg bitów za pomocą bitowego operatora NOT (~
), a następnie AND.Trochę się przełączam
Za pomocą operatora XOR (
^
) można nieco przełączyć.To przełączy
n
trochęnumber
.Trochę sprawdzam
Nie prosiłeś o to, ale równie dobrze mogę to dodać.
Aby to sprawdzić, przesuń liczbę n w prawo, a następnie bitowo ORAZ:
Spowoduje to wstawienie wartości
n
tego bitunumber
do zmiennejbit
.Zmiana n- tego bitu na x
Ustawienie
n
tego bitu na jeden1
lub jeden0
może być osiągnięte za pomocą następujących elementów w implementacji C ++ dla komplementu 2:Bit
n
zostanie ustawiony, jeślix
jest1
, i wyczyszczony, jeślix
jest0
. Jeślix
ma jakąś inną wartość, dostajesz śmieci.x = !!x
wstawi wartość booleanize na 0 lub 1.Aby uniezależnić to od zachowania negacji dopełniacza 2 (gdzie
-1
ustawiono wszystkie bity, w przeciwieństwie do implementacji dopełniacza 1 lub implementacji C ++ znaku / wielkości), użyj negacji bez znaku.lub
Zasadniczo dobrym pomysłem jest używanie typów niepodpisanych do przenośnej manipulacji bitami.
lub
(number & ~(1UL << n))
wyczyścin
bit th i(x << n)
ustawin
th th nax
.Dobrym pomysłem jest również, aby nie kopiować / wklejać kodu, więc wiele osób korzysta z makr preprocesora (np . Odpowiedzi społeczności wiki na dole ) lub jakiegoś rodzaju enkapsulacji.
źródło
bit = (number >> x) & 1
1
jestint
literałem, który jest podpisany. Więc wszystkie operacje tutaj działają na podpisanych numerach, co nie jest dobrze zdefiniowane przez standardy. Standardy nie gwarantują uzupełnienia dwóch lub przesunięcia arytmetycznego, więc lepiej jest użyć1U
.number = number & ~(1 << n) | (x << n);
zmienić n-ty bit na x.Korzystanie z standardowa biblioteka C ++:
std::bitset<N>
.Lub doładowania wersja:
boost::dynamic_bitset
.Nie musisz rzucać własnym:
Wersja Boost umożliwia zestaw bitów w czasie wykonywania w porównaniu ze standardowym zestawem bitów w czasie kompilacji biblioteki .
źródło
Inną opcją jest użycie pól bitowych:
definiuje pole 3-bitowe (w rzeczywistości są to trzy 1-bitowe pola). Operacje bitowe stają się teraz nieco (haha) prostsze:
Aby ustawić lub wyczyścić nieco:
Aby nieco przełączyć:
Trochę sprawdzam:
Działa to tylko z polami bitowymi o stałej wielkości. W przeciwnym razie musisz uciekać się do technik kruszenia bitów opisanych w poprzednich postach.
źródło
Używam makr zdefiniowanych w pliku nagłówkowym do obsługi zestawu bitów i czyszczenia:
źródło
BITMASK_CHECK(x,y) ((x) & (y))
musi być((x) & (y)) == (y)
inaczej zwróci niepoprawny wynik na masce wielobitowej (np.5
vs.3
) / * Witam wszystkich grabarzy1
powinien być(uintmax_t)1
podobny w przypadku, gdy ktoś spróbuje użyć tych makr nalong
typie większym lub większymBITMASK_CHECK_ALL(x,y)
można zaimplementować jako!~((~(y))|(x))
!(~(x) & (y))
Czasami warto użyć
enum
do nazwania bitów:Następnie użyj tych nazw później. To znaczy pisz
ustawić, wyczyścić i przetestować. W ten sposób ukrywasz magiczne liczby przed resztą kodu.
Poza tym popieram rozwiązanie Jeremy'ego.
źródło
clearbits()
funkcję zamiast&= ~
. Dlaczego używasz do tego wyliczenia? Myślałem, że służą one do tworzenia wiązki unikalnych zmiennych z ukrytą dowolną wartością, ale przypisujesz każdemu z nich określoną wartość. Jaka jest więc korzyść w porównaniu do definiowania ich jako zmiennych?enum
s do zestawów stałych pokrewnych ma długą tradycję w programowaniu c. Podejrzewam, że dzięki nowoczesnym kompilatorom jedyną przewagą nad nimiconst short
jest to, że są one wyraźnie zgrupowane. A kiedy chcesz ich do czegoś innego niż maski bitowe, otrzymujesz automatyczną numerację. Oczywiście w c ++ tworzą one również różne typy, co daje trochę dodatkowych statycznych kontroli błędów.enum ThingFlags
wartośćThingError|ThingFlag1
, na przykład?int
. Może to powodować różnego rodzaju subtelne błędy z powodu niejawnej promocji liczb całkowitych lub operacji bitowych na podpisanych typach.thingstate = ThingFlag1 >> 1
wywoła na przykład zachowanie zdefiniowane w implementacji.thingstate = (ThingFlag1 >> x) << y
może wywoływać niezdefiniowane zachowanie. I tak dalej. Aby być bezpiecznym, zawsze przesyłaj do niepodpisanego typu.enum My16Bits: unsigned short { ... };
Z bitops.hip snip-c.zip:
OK, przeanalizujmy rzeczy ...
Powszechnym wyrażeniem, z którym wydaje się, że masz problemy we wszystkich tych przypadkach, jest „(1L << (posn))”. Wszystko to polega na utworzeniu maski z jednym bitem, która będzie działać z dowolnym typem całkowitym. Argument „posn” określa pozycję, w której chcesz bit. Jeśli posn == 0, to wyrażenie będzie miało wartość:
Jeśli posn == 8, oceni, że:
Innymi słowy, po prostu tworzy pole zer z 1 w określonej pozycji. Jedyną trudną częścią jest makro BitClr (), w którym musimy ustawić pojedynczy bit 0 w polu 1. Dokonuje się tego za pomocą dopełniacza 1 tego samego wyrażenia, co oznaczono przez operator tyldy (~).
Po utworzeniu maski jest ona stosowana do argumentu tak, jak sugerujesz, za pomocą operatorów bitowych i (&) lub (|) i xor (^). Ponieważ maska jest długa, makra będą działać równie dobrze na char, short, int lub long.
Najważniejsze jest to, że jest to ogólne rozwiązanie całej klasy problemów. Oczywiście jest możliwe, a nawet właściwe przepisać odpowiednik któregokolwiek z tych makr za pomocą jawnych wartości maski za każdym razem, gdy jest to potrzebne, ale dlaczego? Pamiętaj, że zastępowanie makr zachodzi w preprocesorze, więc wygenerowany kod będzie odzwierciedlał fakt, że kompilator uważa, że wartości są stałe - tzn. Użycie makr uogólnionych jest równie skuteczne, jak „ponowne wymyślenie koła” za każdym razem, gdy trzeba zrobić trochę manipulacji.
Nie przekonany? Oto kod testowy - użyłem Watcom C z pełną optymalizacją i bez użycia _cdecl, aby wynikowy demontaż był jak najbardziej czysty:
---- [TEST.C] ----------------------------------------- -----------------------
---- [TEST.OUT (zdemontowane)] -------------------------------------- ---------
---- [finis] ------------------------------------------- ----------------------
źródło
arg
jestlong long
.1L
musi być najszerszym możliwym typem, więc(uintmax_t)1
. (Możesz uciec1ull
)Użyj operatorów bitowych:
&
|
Aby ustawić ostatni bit w
000b
:Aby sprawdzić ostatni bit w
foo
:Aby wyczyścić ostatni bit w
foo
:Użyłem
XXXb
dla jasności. Prawdopodobnie będziesz pracować z reprezentacją HEX, w zależności od struktury danych, w której pakujesz bity.źródło
foo = foo ^ MY_MASK
foo = foo & ~MY_MASK
Dla początkujących chciałbym wyjaśnić nieco więcej przykładem:
Przykład:
&
Operator służy sprawdzeniu bitu:Przełącz lub odwróć:
|
operator: ustaw bitźródło
Oto moje ulubione bitowe makro arytmetyczne, które działa na każdym typie tablicy liczb całkowitych bez znaku od
unsigned char
dosize_t
(który jest największym typem, który powinien być wydajny w pracy):Aby ustawić trochę:
Aby trochę wyczyścić:
Aby nieco przełączyć:
Aby trochę przetestować:
itp.
źródło
BITOP(array, bit++, |=);
z pętli najprawdopodobniej nie spełni oczekiwań osoby dzwoniącej.BITCELL(a,b) |= BITMASK(a,b);
(zarówno odbiorua
jako argument do określenia wielkości, ale ten nigdy nie oceniaća
, ponieważ pojawia się tylko wsizeof
).(size_t)
obsada wydaje się być nie tylko, aby zapewnić pewne niepodpisane matematyki z%
. Może(unsigned)
tam(size_t)(b)/(8*sizeof *(a))
Niepotrzebnie może zmniejszyćb
przed podziałem. Tylko problem z bardzo dużymi tablicami bitów. Wciąż ciekawe makro.Ponieważ jest to oznaczone jako „osadzone”, założę, że używasz mikrokontrolera. Wszystkie powyższe sugestie są prawidłowe i działają (odczyt-modyfikacja-zapis, związki, struktury itp.).
Jednak podczas serii debugowania opartego na oscyloskopie byłem zaskoczony, gdy zauważyłem, że metody te mają znaczny narzut w cyklach procesora w porównaniu z zapisywaniem wartości bezpośrednio w rejestrach PORTnSET / PORTnCLEAR mikroprocesora, co stanowi istotną różnicę w przypadku ciasnych pętli / wysokich Częstotliwość przełączania pinów ISR.
Dla tych, którzy nie są zaznajomieni: W moim przykładzie mikro ma ogólny rejestr stanu PORTn stanu pinów, który odzwierciedla styki wyjściowe, więc wykonanie PORTn | = BIT_TO_SET powoduje odczyt i modyfikację zapisu w tym rejestrze. Jednak rejestry PORTnSET / PORTnCLEAR przyjmują „1”, co oznacza „proszę zrobić ten bit 1” (SET) lub „proszę zrobić ten bit zero” (CLEAR) i „0”, co oznacza „zostawić pin w spokoju”. tak więc otrzymujesz dwa adresy portów w zależności od tego, czy ustawiasz, czy kasujesz bit (nie zawsze jest to wygodne), ale znacznie szybszą reakcję i mniejszy złożony kod.
źródło
volatile
a zatem kompilator nie jest w stanie wykonać żadnych optymalizacji kodu obejmującego takie rejestry. Dlatego dobrą praktyką jest dezasemblowanie takiego kodu i sprawdzanie, jak wyszło na poziomie asemblera.Podejście do pola bitowego ma inne zalety na wbudowanej arenie. Możesz zdefiniować strukturę, która będzie mapowana bezpośrednio na bity w konkretnym rejestrze sprzętowym.
Musisz być świadomy kolejności pakowania bitów - myślę, że to najpierw MSB, ale może to zależeć od implementacji. Sprawdź także, w jaki sposób procedury obsługi kompilatora przekraczają granice bajtów.
Następnie możesz czytać, pisać, testować poszczególne wartości jak poprzednio.
źródło
Sprawdź trochę w dowolnym miejscu w zmiennej dowolnego typu:
Przykładowe użycie:
Uwagi: Został zaprojektowany jako szybki (biorąc pod uwagę jego elastyczność) i nierozgałęziony. Skutkuje to wydajnym kodem maszynowym SPARC podczas kompilacji Sun Studio 8; Przetestowałem to również przy użyciu MSVC ++ 2008 na amd64. Możliwe jest tworzenie podobnych makr do ustawiania i czyszczenia bitów. Kluczową różnicą tego rozwiązania w porównaniu z wieloma innymi tutaj jest to, że działa on dla dowolnej lokalizacji w prawie każdym typie zmiennej.
źródło
Ogólniej, w przypadku bitmap o dowolnej wielkości:
źródło
CHAR_BIT
jest już zdefiniowane przezlimits.h
, nie musisz wstawiać własnegoBITS
(a tak naprawdę pogarszasz swój kod)Ten program służy do zmiany dowolnego bitu danych z 0 na 1 lub 1 na 0:
źródło
Jeśli robisz dużo kręcenia, możesz chcieć użyć masek, które przyspieszą. Poniższe funkcje są bardzo szybkie i wciąż elastyczne (pozwalają na kręcenie bitów w mapach bitowych o dowolnym rozmiarze).
Uwaga: aby ustawić bit „n” w 16-bitowej liczbie całkowitej, wykonaj następujące czynności:
Od Ciebie zależy, czy liczba bitów będzie w zakresie przekazywanej mapy bitów. Zwróć uwagę, że w przypadku małych procesorów endian, które bajty, słowa, dwory, qwords itp. Poprawnie odwzorowują się w pamięci (główny powód, dla którego małe procesory endianowe są „lepsze” niż procesory big-endian, ach, czuję, że nadchodzi wojna płomieni na...).
źródło
Użyj tego:
źródło
Rozwijając
bitset
odpowiedź:źródło
Jeśli chcesz wykonać tę całą operację z programowaniem C w jądrze Linuksa , sugeruję użycie standardowych interfejsów API jądra Linux.
Zobacz https://www.kernel.org/doc/htmldocs/kernel-api/ch02s03.html
Uwaga: Tutaj cała operacja odbywa się w jednym kroku. Gwarantuje to, że są one atomowe nawet na komputerach SMP i są przydatne do zachowania spójności między procesorami.
źródło
Visual C 2010 i być może wiele innych kompilatorów ma wbudowane bezpośrednie wsparcie dla operacji boolowskich. Trochę ma dwie możliwe wartości, podobnie jak boolean, więc zamiast tego możemy używać boolanów - nawet jeśli zajmują więcej miejsca niż jeden bit w pamięć w tej reprezentacji. To działa, nawet
sizeof()
operator działa poprawnie.Więc na twoje pytanie
IsGph[i] =1
, lubIsGph[i] =0
ułatw sobie ustawianie i usuwanie booli.Aby znaleźć niedrukowalne znaki:
Uwaga: w tym kodzie nie ma nic „specjalnego”. Traktuje to trochę jak liczbę całkowitą - co technicznie jest. 1-bitowa liczba całkowita, która może pomieścić 2 wartości i tylko 2 wartości.
Kiedyś użyłem tego podejścia, aby znaleźć duplikaty rekordów pożyczek, gdzie numer_ pożyczki był kluczem ISAM, używając 6-cyfrowego numeru pożyczki jako indeksu w tablicy bitów. Niesamowicie szybki i po 8 miesiącach udowodnił, że system mainframe, z którego otrzymywaliśmy dane, w rzeczywistości działał nieprawidłowo. Prostota tablic bitowych sprawia, że pewność ich poprawności jest bardzo wysoka - na przykład w stosunku do wyszukiwania.
źródło
bool
. Może nawet 4 bajty dla konfiguracji C89, które używająint
do implementacjibool
Użyj jednego z operatorów zdefiniowanych tutaj .
Aby ustawić bit, należy użyć
int x = x | 0x?;
gdzie gdzie?
jest pozycja bitu w formie binarnej.źródło
0x
jest przedrostkiem literału w systemie szesnastkowym, a nie binarnym.Oto kilka makr, których używam:
źródło
Zastosowana zmienna
wartość -
Pozycja danych - pozycja bitu, którą chcemy ustawić, wyczyścić lub przełączyć.
Ustaw trochę:
Wyczyść trochę:
Przełącz trochę:
źródło
źródło
check_nth_bit
może byćbool
.Jak się trochę dostać?
nth
odrobinę num prawej zmianynum
,n
razy. Następnie wykonaj bitowe AND&
z 1.Jak to działa?
Jak ustawić trochę?
n
razy. Następnie wykonaj bitową operację LUB za|
pomocąnum
.Jak to działa?
Jak trochę wyczyścić?
n
razy tj1 << n
.~ (1 << n)
.&
z powyższym wynikiem inum
. Powyższe trzy kroki razem można zapisać jakonum & (~ (1 << n))
;Jak to działa?
Jak trochę przełączyć?
Aby nieco przełączyć, używamy bitowego XOR
^
operatora . Bitowy operator XOR ocenia na 1, jeśli odpowiadający mu bit obu argumentów jest inny, w przeciwnym razie ocenia na 0.Co oznacza, że należy trochę przełączyć, musimy wykonać operację XOR z bitem, który chcesz przełączyć i 1.
Jak to działa?
0 ^ 1 => 1
.1 ^ 1 => 0
.Zalecana lektura - Bitowe ćwiczenia operatora
źródło
Aby rozwiązać problem typowego pułapki kodowania podczas próby utworzenia maski:
1
nie zawsze jest wystarczająco szerokiJakie problemy się zdarzają, gdy
number
jest szerszy niż1
?x
może być zbyt wielki dla zmiany1 << x
prowadzącej do niezdefiniowanego zachowania (UB). Nawet jeślix
nie jest zbyt wielki,~
może nie odwrócić wystarczającej liczby najbardziej znaczących bitów.Aby zapewnić, że 1 jest wystarczająco szeroki:
Kod może użyć
1ull
lub pedantycznie(uintmax_t)1
i pozwolić kompilatorowi zoptymalizować.Lub obsada - co powoduje problemy z kodowaniem / przeglądem / konserwacją, dzięki czemu obsada jest poprawna i aktualna.
Lub delikatnie promuj
1
poprzez wymuszanie operacji matematycznej, która jest co najmniej tak szeroka jak rodzajnumber
.Jak w większości bitowe manipulacji, najlepiej do pracy z niepodpisanych typów zamiast podpisanych te
źródło
number |= (type_of_number)1 << x;
nienumber |= (number*0 + 1) << x;
należy ustawiać bitu znaku typu podpisanego ... Właściwie nie jestnumber |= (1ull << x);
. Czy istnieje przenośny sposób na zrobienie tego według pozycji?Wersja szablonowa C ++ 11 (umieszczona w nagłówku):
źródło
;
definicje funkcji?)(variable & bits == bits)
?((variable & bits) == bits)
std::bitset
w c ++ 11Ten program jest oparty na powyższym rozwiązaniu @ Jeremy. Jeśli ktoś chce szybko się pobawić.
źródło
Wypróbuj jedną z tych funkcji w języku C, aby zmienić bit n:
Lub
Lub
źródło
value << n
może powodować niezdefiniowane zachowanie