Jak ustawić, wyczyścić i przełączyć jeden bit?


Odpowiedzi:


3595

Ustawienie trochę

Użyj bitowego operatora OR ( |), aby ustawić bit.

number |= 1UL << n;

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 ^= (-(unsigned long)x ^ number) & (1UL << n);

lub

unsigned long 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.


127
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>.

Lub doładowania wersja: boost::dynamic_bitset.

Nie musisz rzucać własnym:

#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

Wersja Boost umożliwia zestaw bitów w czasie wykonywania w porównaniu ze standardowym zestawem bitów w czasie kompilacji biblioteki .


34
+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).
Martin York,

248

Inną opcją jest użycie pól bitowych:

struct bits {
    unsigned int a:1;
    unsigned int b:1;
    unsigned int c:1;
};

struct bits mybits;

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.


68
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))

17
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
MM

2
BITMASK_CHECK_ALL(x,y)można zaimplementować jako!~((~(y))|(x))
Handy999,

3
@ Handy999 Nieco łatwiej jest zrozumieć, dlaczego to działa po zastosowaniu prawa De Morgana i ponownym umówieniu się na dostawę!(~(x) & (y))
Tavian Barnes

114

Czasami warto użyć enumdo nazwania bitów:

enum ThingFlags = {
  ThingMask  = 0x0000,
  ThingFlag0 = 1 << 0,
  ThingFlag1 = 1 << 1,
  ThingError = 1 << 8,
}

Następnie użyj tych nazw później. To znaczy pisz

thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}

ustawić, wyczyścić i przetestować. W ten sposób ukrywasz magiczne liczby przed resztą kodu.

Poza tym popieram rozwiązanie Jeremy'ego.


1
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 { ... };
Aiken Drum

47

Z bitops.hip snip-c.zip:

/*
**  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, 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ść:

0000 0000 0000 0000 0000 0000 0000 0001 binary.

Jeśli posn == 8, oceni, że:

0000 0000 0000 0000 0000 0001 0000 0000 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:

---- [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 (zdemontowane)] -------------------------------------- ---------

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] ------------------------------------------- ----------------------


3
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.


7
W C. nie ma zapisu binarnego. Binarne stałe całkowite są niestandardowym rozszerzeniem.
Lundin

Użyj XOR, aby nieco przełączyć:foo = foo ^ MY_MASK
Peter L

Użyj przycisku NOT, aby odwrócić maskę, aby wyczyścić:foo = foo & ~MY_MASK
Peter L

32

Dla początkujących chciałbym wyjaśnić nieco więcej przykładem:

Przykład:

value is 0x55;
bitnum : 3rd.

&Operator służy sprawdzeniu bitu:

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)

Przełącz lub odwróć:

0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)

| operator: ustaw bit

0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)

26

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)))))

Aby ustawić trochę:

BITOP(array, bit, |=);

Aby trochę wyczyścić:

BITOP(array, bit, &=~);

Aby nieco przełączyć:

BITOP(array, bit, ^=);

Aby trochę przetestować:

if (BITOP(array, bit, &)) ...

itp.


5
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.

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;

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.


2
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:

#define bit_test(x, y)  ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )

Przykładowe użycie:

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;
}

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.


20

Ogólniej, w przypadku bitmap o dowolnej wielkości:

#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)))

2
CHAR_BITjest już zdefiniowane przez limits.h, nie musisz wstawiać własnego BITS(a tak naprawdę pogarszasz swój kod)
MM

14

Ten program służy do zmiany dowolnego bitu danych z 0 na 1 lub 1 na 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);
        }
    }
}

14

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).

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;
}

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...).


2
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:

int ToggleNthBit ( unsigned char n, int num )
{
    if(num & (1 << n))
        num &= ~(1 << n);
    else
        num |= (1 << n);

    return num;
}

5
Cóż, wykorzystuje nieefektywne rozgałęzianie.
asdf

3
@asdf pracy kompilatora jest do produkcji najbardziej wydajny binarny, praca programisty jest pisanie kodu jasne
MM

3
To dobra demonstracja testowania, ustawiania i czyszczenia określonego bitu. Jest to jednak bardzo złe podejście do nieco przełączania.
Ben Voigt

10

Rozwijając bitsetodpowiedź:

#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;
}

10

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

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

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.


9

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.

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);
}

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”.
Ben Voigt

6

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.


2
0xjest przedrostkiem literału w systemie szesnastkowym, a nie binarnym.
Ben Voigt

5

Oto kilka makr, których używam:

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)

5

Zastosowana zmienna

int value, pos;

wartość -
Pozycja danych - pozycja bitu, którą chcemy ustawić, wyczyścić lub przełączyć.

Ustaw trochę:

value = value | 1 << pos;

Wyczyść trochę:

value = value & ~(1 << pos); 

Przełącz trochę:

value = value ^ 1 << pos;

5
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);
}

Typem zwrotu check_nth_bitmoże być bool.
Xeverous

@ Xeverous tak, to zależy od intencji dzwoniących
Sazzad Hissain Khan

5

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ć?

  1. Aby uzyskać nthodrobinę num prawej zmiany num, nrazy. Następnie wykonaj bitowe AND &z 1.
bit = (num >> n) & 1;

Jak to działa?

       0011 0111 (55 in decimal)
    >>         4 (right shift 4 times)
-----------------
       0000 0011
     & 0000 0001 (1 in decimal)
-----------------
    => 0000 0001 (final result)

Jak ustawić trochę?

  1. Aby ustawić konkretny bit liczby. Shift w lewo 1 nrazy. Następnie wykonaj bitową operację LUB za |pomocą num.
num |= (1 << n);    // Equivalent to; num = (1 << n) | num;

Jak to działa?

       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
       0001 0000
     | 0011 0111 (55 in decimal)
-----------------
    => 0001 0000 (final result)

Jak trochę wyczyścić?

  1. Przesunięcie w lewo 1, nrazy tj 1 << n.
  2. Wykonaj bitowe uzupełnienie z powyższym wynikiem. Tak, że n-ty bit zostanie rozbrojony, a reszta bitu zostanie ustawiona tj~ (1 << n) .
  3. Na koniec wykonaj bitową operację AND &z powyższym wynikiem i num. Powyższe trzy kroki razem można zapisać jako num & (~ (1 << n));

Kroki, aby trochę wyczyścić

num &= (~(1 << n));    // Equivalent to; num = num & (~(1 << n));

Jak to działa?

       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
     ~ 0001 0000
-----------------
       1110 1111
     & 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)

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.

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.
       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
       0001 0000
     ^ 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)

Zalecana lektura - Bitowe ćwiczenia operatora


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/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

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):

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;}
}

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ć.

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

-2

Wypróbuj jedną z tych funkcji w języku C, aby zmienić bit n:

char bitfield;

// Start at 0th position

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}

Lub

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}

Lub

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 << nmoże powodować niezdefiniowane zachowanie
MM
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.