Cum setați, ștergeți și comutați un singur bit?

Cum setați, ștergeți și comutați puțin?


person JeffV    schedule 07.09.2008    source sursă
comment
citiți asta: graphics.stanford.edu/~seander/bithacks.html și, când o vei stăpâni, citește-l pe acesta: realtimecollisiondetection.net/blog/?p=78   -  person ugasoft    schedule 18.09.2008
comment
Ați putea fi, de asemenea, interesat să consultați The Bit Twiddler, Bit Twiddling Hacks și Algoritmii magici agregați.   -  person    schedule 05.01.2009


Răspunsuri (27)


Stabilirea un pic

Utilizați operatorul SAU pe biți (|) pentru a seta un bit.

number |= 1UL << n;

Aceasta va seta al nlea bit din number. n ar trebui să fie zero, dacă doriți să setați 1st bit și așa mai departe până la n-1, dacă doriți să setați nth bit.

Folosiți 1ULL dacă number este mai lat decât unsigned long; promovarea lui 1UL << n nu are loc decât după evaluarea lui 1UL << n în cazul în care comportamentul nedefinit se schimbă cu mai mult decât lățimea unui long. Același lucru este valabil și pentru toate celelalte exemple.

Curățind puțin

Utilizați operatorul AND pe biți (&) pentru a șterge puțin.

number &= ~(1UL << n);

Asta va șterge al nal-lea bit din number. Trebuie să inversați șirul de biți cu operatorul NOT pe biți (~), apoi AND it.

Comutând puțin

Operatorul XOR (^) poate fi folosit pentru a comuta puțin.

number ^= 1UL << n;

Aceasta va comuta al nal-lea bit din number.

Verifică puțin

Nu ai cerut asta, dar aș putea la fel de bine să-l adaug.

Pentru a verifica puțin, mutați numărul n la dreapta, apoi pe biți și acesta:

bit = (number >> n) & 1U;

Aceasta va pune valoarea celui de-al 25-lea bit al lui number în variabila bit.

Schimbarea nal-lea bit în x

Setarea celui de-al 28_lea bit la 1 sau la 0 poate fi realizată cu următoarele pe o implementare C++ în complement a 2:

number ^= (-x ^ number) & (1UL << n);

Bitul n va fi setat dacă x este 1 și șters dacă x este 0. Dacă x are o altă valoare, primești gunoi. x = !!x îl va booleaniza la 0 sau 1.

Pentru a face acest lucru independent de comportamentul de negație al complementului 2 (unde -1 are toți biții setați, spre deosebire de implementarea complementului 1 sau C++ cu semn/magnitudine), utilizați negația fără semn.

number ^= (-(unsigned long)x ^ number) & (1UL << n);

or

unsigned long newbit = !!x;    // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);

În general, este o idee bună să folosiți tipuri nesemnate pentru manipularea biților portabili.

or

number = (number & ~(1UL << n)) | (x << n);

(number & ~(1UL << n)) va șterge bitul nși (x << n) va seta bitul nla x.

De asemenea, este, în general, o idee bună să nu copiați/lipiți codul în general și atât de mulți oameni folosesc macrocomenzi de preprocesor (cum ar fi răspunsul wiki comunității mai jos) sau un fel de încapsulare.

person Paige Ruten    schedule 07.09.2008
comment
Aș dori să remarc că pe platformele care au suport nativ pentru set/clear de biți (de exemplu, microcontrolere AVR), compilatoarele vor traduce adesea „myByte |= (1 ‹‹ x)” în instrucțiunile native set/clear de biți ori de câte ori x este o constantă, de exemplu: (1 ‹‹ 5), sau const fără semn x = 5. - person Aaron; 17.09.2008
comment
bit = număr & (1 ‹‹ x); nu va pune valoarea bitului x în bit decât dacă bitul are tipul _Bool (‹stdbool.h›). În caz contrar, bit = !!(număr & (1 ‹‹ x)); voi.. - person Chris Young; 16.11.2008
comment
Aceste tipuri de soluții funcționează numai atunci când variabila țintă este de tip integral. Se pot face soluții mai generale pentru a funcționa pe alte tipuri de țintă utile, cum ar fi matrice. - person John Zwinck; 04.01.2009
comment
Sunt doar eu, dar aș prefera să pun în paranteză expresiile de deplasare a bitului (similar cu ceea ce a făcut @Aaron mai sus). - person Happy Green Kid Naps; 09.05.2013
comment
BTW, jocul de biți de aici va eșua în tăcere dacă number este mai lat decât int - person anatolyg; 09.05.2013
comment
de ce nu-l schimbi pe ultimul cu bit = (number >> x) & 1 - person aaronman; 26.06.2013
comment
@Aaron: x86 are testul de biți nativ și instrucțiunile de setare/ștergere/complementare - person phuclv; 01.08.2013
comment
Cred că este o problemă la notație. Să presupunem că am 0011 (3 zecimală) și vreau să verific dacă al doilea bit este setat, de exemplu, cel îngroșat: 0 0 1 1. Cum vă referiți la X? Va fi al 2-lea sau al 1-lea bit în funcție de notația dvs., pentru că dacă este al 2-lea bit, cred că sugestia dvs. nu va funcționa, de exemplu, deoarece veți schimba 1 de două ori și veți obține 100 - care nu va produce al 2-lea bit ca am definit mai sus. Nu-i aşa? - person ; 14.09.2013
comment
1 este un literal int, care este semnat. Deci toate operațiunile de aici funcționează pe numere semnate, ceea ce nu este bine definit de standarde. Standardele nu garantează complementul în doi sau schimbarea aritmetică, așa că este mai bine să folosiți 1U. - person Siyuan Ren; 10.12.2013
comment
Pentru a clarifica comentariul lui Anatolyg, constanta 1 fără modificatori este definită ca fiind un int semnat. Pentru ca acest lucru să funcționeze corect pentru toate variabilele, utilizați în schimb 1ULL. - person JonS; 08.07.2014
comment
@JonS pentru toate variabilele de până la unsigned long long oricum... pot exista extensii definite de implementare, cum ar fi __int128 . Pentru a fi ultra-sigur (uintmax_t)1 << x - person M.M; 07.02.2015
comment
Prefer number = number & ~(1 << n) | (x << n); pentru schimbarea bitului n-lea la x. - person jiasli; 24.03.2015
comment
@Jiashuo Li: această declarație va eșua dacă number este mai mare decât int și n este mai mare sau egal cu numărul de biți dintr-un int. Invocă chiar un comportament nedefinit în acest caz. number = number & ~((uintmax_t)1 << n) | ((uintmax_t)x << n); este o expresie generică care ar trebui să funcționeze pentru toate dimensiunile de number, dar poate genera cod urât și ineficient pentru dimensiuni mai mici. - person chqrlie; 24.03.2015
comment
Legat de verificarea unui pic: de ce să nu folosiți pur și simplu numărul și 0x01 pentru a verifica primul bit, numărul și 0x08 pentru al patrulea etc. Imho, mai frumos. - person Anonymous; 08.04.2015
comment
Utilizarea operațiunilor precum -x nu este sigură, deoarece standardul C permite numerelor întregi cu semn să fie (de exemplu) mărimea semnului, complementul celor sau orice alt sistem care poate exprima intervalul necesar, ceea ce înseamnă că -x este nu trebuie să fie la fel ca (~x) + 1. Aceasta nu este o afacere atât de mare pentru arhitecturile moderne, dar nu știi niciodată ce va face un compilator de optimizare suficient de inteligent cu codul tău. - person Kevin; 18.01.2016
comment
De ce folosim ~ în loc de !? - person 71GA; 30.01.2017
comment
@Anonim. Folosim măști oricum, astfel încât dacă trebuie vreodată să facem mai mult de o extensie este evident? Notă laterală, majuscule/minuscule în EBCDIC este de 1 bit. - person mckenzm; 09.03.2017
comment
Ultimul exemplu nu presupune reprezentarea în complement de doi? Nu-i așa că este rău / neportabil, cum vrei să-i spui? - person Patrick Roberts; 15.06.2017
comment
@71GA: Negația pe biți (~) inversează toți biții, deci ~0xFFF0FFFF este 0x000F0000. Boolean not (!) dă 0 dacă valoarea este diferită de zero sau 1 dacă valoarea este zero, deci !0xFFF0FFFF este 0x00000000. - person Adrian McCarthy; 05.09.2017
comment
@PatrickRoberts: Da, necesită complementul 2. În complementul unu, -1 este 0b1111..1110. (Toate cele este -0). C++ permite, de asemenea, reprezentări semn/magnitudine întregi, unde -x doar răsturnează bitul de semn. Am actualizat acest răspuns pentru a sublinia asta. Este foarte bine dacă țintiți doar implementările C++ complementare 2. Nu este UB, este doar definit de implementare, deci este necesar să funcționeze corect pe implementări în care definesc numerele întregi cu semn ca complement 2. De asemenea, am schimbat constantele la 1UL, cu o notă că 1ULL poate fi necesar. - person Peter Cordes; 10.11.2017
comment
@Kevin: Este garantat că va fi sigur pe o implementare C++ care utilizează complementul 2. Implementarea este definită, nu UB. Lucrul de care să vă faceți griji este portabilitatea la complementul sau semnul/magnitudinea cuiva, nu optimizarea compilatoarelor. - person Peter Cordes; 10.11.2017
comment
@Peter -x este UB dacă x este INT_MIN. - person Kevin; 10.11.2017
comment
@Kevin: oh, corect, ar trebui să fie -(unsigned long)x, ceea ce ar ocoli reprezentarea întregului semnat. (baza 2 fără semn se potrivește cu semantica complementului 2.) Dar funcționează corect numai dacă x este 0 sau 1. Amintiți-vă că setăm bitul n la x. - person Peter Cordes; 11.11.2017
comment
@PeterCordes: Este pe deplin rezonabil. Dar când un dezvoltator îmi spune că acest invariant va fi întotdeauna! Devin foarte nervos. - person Kevin; 11.11.2017
comment
@Kevin: Am decis să omit orice mențiune despre UB cu depășire semnată în ultima mea editare, deoarece doriți să utilizați oricum nesemnat pentru 0U - 1U -› all-one portabil. Cum arată acum? Am încercat să păstrez răspunsul simplu. Am menționat că poate fi necesar să booleanizați cu !!x. Dacă acesta a fost propriul meu răspuns, s-ar putea să inserez mai mult text despre folosirea întotdeauna nesemnată, dar doar păstrez acest vechi răspuns canonic. (Jeremy, sper că vă plac modificările, s-ar putea să doriți să editați pentru a le pune modificările în propriile cuvinte sau orice altceva doriți să spuneți aproape 9 ani mai târziu.) - person Peter Cordes; 11.11.2017
comment
Tocmai am observat în istoricul editărilor că secțiunea nal-lea în x a fost adăugată de o altă editare terță parte și nu a fost opera lui Jeremy în primul rând. - person Peter Cordes; 11.11.2017
comment
Metoda dvs. de a schimba al n-lea bit la x este valabilă și pentru C90? Și acest răspuns este valabil și pentru numerele nesemnate? - person avivgood2; 22.04.2020
comment
M-am întâlnit oarecum rar și întotdeauna m-am gândit că ar trebui să vin cu o singură expresie pentru a face operația set bit to value. În general, fac save state + unconditionally set/clear + restore state în hardware. Oricum, am dat întâmplător de asta și cu siguranță voi fura expresia de mai sus. - person sherrellbc; 15.01.2021
comment
number = (number | (1UL << n)) ^ (!x << n) Simplificat pentru a elimina un nu logic și un nu pe biți - person Shogan Aversa-Druesne; 18.01.2021
comment
expresia de mai sus ^^^^^ permițând lui x să fie nerestricționat, x poate fi 0 sau orice număr pozitiv pentru adevărat și nu este legat de 0 și 1 - person Shogan Aversa-Druesne; 18.01.2021
comment
Uneori mă întreb dacă nu o simplă declarație if/else-ar fi mai eficientă atunci când schimbi n-lea bit la x. Nu ar genera number = (number & ~(1UL << n)) | (x << n); deplasări de doi biți, un flip de un octet, unul AND, unul SAU și, în sfârșit, o atribuire. În timp ce if (x) { number |= (1 << n); } else { number &= ~(1 << n); } ar genera o comparație, o deplasare de biți, fie un SAU sau un AND cu o schimbare de octet, precum și o atribuire. 6 vs 5 operațiuni în cel mai rău caz (ștergerea bitului). 6 vs 4 la setarea bitului. Mult mai lizibil. Dar poate că unele operațiuni sunt mai costisitoare? - person Smartskaft2; 02.05.2021
comment
@Smartskaft2 problema este că ramificarea cu if/else poate fi mult mai costisitoare. Căutați eșecul predicției ramurilor sau consultați această întrebare: stackoverflow.com/q/11227809 - person Angra Mainyu; 03.05.2021
comment
Erorile de predicție ale filialei @AngraMainyu sunt neglijabil de mici: godbolt.org/z/vascq9Khc soluția dacă este chiar 1.2 ori mai repede. - person JulianH; 26.05.2021
comment
@JulianH Mulțumesc pentru testare. Dar în acel test avem un model fix de alternanță adevărat/fals în condiția de ramificare, pe care predictorul de ramificare probabil îl va prezice perfect după primele câteva iterații. Deci testul nu măsoară de fapt eșecul predicției ramurilor. - person Angra Mainyu; 27.05.2021
comment
@AngraMainyu CPU nu prezice perfect așa cum ați spus, deoarece există o diferență de accelerare între x alternativ și nealternant, cu toate acestea, atunci când se distribuie aleatoriu valoarea lui x, soluția non-if este de două ori mai rapidă decât soluția if: godbolt.org/z/5aEbKchzf Deci, da, ai dreptate, predicția ramurilor distruge performanța. - person JulianH; 28.05.2021

Folosind biblioteca standard C++: std::bitset<N>.

Sau versiunea Boost: boost::dynamic_bitset.

Nu este nevoie să vă rotiți singur:

#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

Versiunea Boost permite un set de biți de dimensiunea de rulare în comparație cu o bibliotecă standard de dimensiunea de compilare set de biți.

person Martin York    schedule 18.09.2008
comment
+1. Nu că std::bitset este utilizabil din C, dar deoarece autorul și-a etichetat întrebarea cu C++, AFAIK, răspunsul dvs. este cel mai bun de aici... std::vector‹bool› este o altă modalitate, dacă cineva îl știe avantaje și dezavantaje ale acestuia - person paercebal; 19.09.2008
comment
Frumos, Martin! Puteți folosi chiar și o enumerare pentru a „indexa” biții: enum { cEngineOn, cDoorsOpen, cAircoOn }; std::bitset‹ cNBBITS › mybits; mybits[ cEngineOn ].set(); const bool cbDoorOpen = mybits[ cDoorsOpen ]; ... - person xtofl; 27.09.2008
comment
printre răspunsurile de aici, cred că acesta este cel mai bun mod de a gestiona biții... dar cred că spiritul întrebării a fost cum să manipulați biții manual. totusi, +vot :) - person moogs; 15.10.2008
comment
@paercebal: vector‹bool› nu este foarte eficient, deoarece bool în C++ ocupă un octet întreg de spațiu, în loc de 1 bit... - person andrewdotnich; 13.11.2008
comment
@andrewdotnich: vector‹bool› este (din păcate) o specializare care stochează valorile ca biți. Consultați gotw.ca/publications/mill09.htm pentru mai multe informații... - person Niklas; 12.12.2008
comment
Poate că nimeni nu a menționat-o pentru că aceasta a fost etichetată încorporat. În majoritatea sistemelor încorporate evitați STL ca ciuma. Iar suportul pentru creștere este probabil o pasăre foarte rară de observat printre majoritatea compilatoarelor încorporate. - person Lundin; 18.08.2011
comment
@Lundin: Punctul unu nu este adevărat (unele lucruri din STL sunt evitate, dar o declarație generală ca aceasta este doar la general, std::bitset este bine și nu costă nimic de utilizat.). Punctul doi Boost::dynamic_bitset nu depinde de nimic și poate fi folosit cu ușurință. - person Martin York; 18.08.2011
comment
@Martin Este foarte adevărat. Pe lângă elementele ucigașe de performanță specifice, cum ar fi STL și șabloane, multe sisteme încorporate chiar evită complet bibliotecile standard, deoarece sunt atât de greu de verificat. Majoritatea ramurilor încorporate adoptă standarde precum MISRA, care necesită instrumente statice de analiză a codului (orice profesioniști în software ar trebui să folosească astfel de instrumente, nu doar oamenii încorporați). În general, oamenii au lucruri mai bune de făcut decât să ruleze analiza statică prin întreaga bibliotecă standard - dacă codul sursă este chiar disponibil pentru ei pe compilatorul specific. - person Lundin; 19.08.2011
comment
@Lundin: Declarațiile tale sunt excesiv de largi (deci inutil să argumentezi). Sunt sigur că pot găsi situații în care sunt adevărate. Acest lucru nu schimbă punctul meu inițial. Ambele clase sunt perfecte pentru a fi utilizate în sistemele încorporate (și știu sigur că sunt folosite). Punctul dvs. inițial despre faptul că STL/Boost nu este utilizat pe sistemele încorporate este, de asemenea, greșit. Sunt sigur că există sisteme care nu le folosesc și chiar și sistemele care le folosesc sunt folosite judicios, dar a spune că nu sunt folosite este pur și simplu nu este corect (pentru că există sisteme în care sunt folosite). - person Martin York; 19.08.2011
comment
@jons34yp: Documentația SGI este mai conică și, în general, are mai puține greșeli decât cppreference. - person Martin York; 24.04.2013
comment
Subiectul afirmă clar limbajul C, nu C++. În mod clar, acesta este un răspuns incorect, deci cum naiba a primit peste 120 de voturi pozitive?! - person mloskot; 10.09.2013
comment
@mloskot: Întrebarea afirmă clar C++. Este în eticheta de mai jos întrebarea ce limbi sunt valide. A primit peste 120 de voturi pentru că oamenii care pot citi citesc întrebarea și au evaluat corect în contextul etichetelor. :-) Ohh. Și este simplu. - person Martin York; 11.09.2013
comment
@LokiAstari Îndrăznesc să susțin fie că eticheta este incorectă, fie că subiectul și corpul este imprecis. Aceasta este o problemă importantă, deoarece duce la ca întrebările SO să devină vagi, deci într-adevăr inutile. - person mloskot; 11.09.2013
comment
Tot ce știu este că de fiecare dată când încerc să compilez ceva realizat în C++, Boost devine noul cel mai mare dușman al meu. Oricum am votat pozitiv. - person sudo; 13.08.2014
comment
@9000: De ce? Nu ar trebui să faci nimic cu boost. Aceasta este o bibliotecă numai antet și instalarea boost ar trebui să fie lucruri de gestionare a pachetelor sudo apt-get install boost-devel - person Martin York; 13.08.2014
comment
Întrebarea pusă pentru C/C++, deci pentru a fi conform cu ambele lumi, cred că STL nu este aplicabil aici. - person Luis Colorado; 30.09.2014
comment
@LuisColorado: Totuși, a nu vorbi despre asta ar însemna să nu ai informațiile disponibile pentru utilizatori. Ideea nu este să vă limitați răspunsul, ci să oferiți o soluție. OP va decide dacă este relevant prin bifarea celui mai bun răspuns. Votați sus dacă credeți că este un răspuns util comunității sau în jos dacă nu are valoare pentru comunitate. - person Martin York; 30.09.2014
comment
@LuisColorado: Nu sunt de acord. A rezolvat problema într-un mod pe care alții nu l-au menționat. Desigur, a fost pentru un subset de oameni care au folosit C++, dar întrebarea a fost etichetată ca C++ și, prin urmare, un răspuns valid. - person Martin York; 02.10.2014
comment
@LokiAstari, și acesta este motivul pentru care tocmai am făcut un comentariu, niciun vot negativ, nimic altceva... doar un comentariu. - person Luis Colorado; 05.10.2014
comment
@MartinYork Lumea încorporată este largă; dar dacă STL-ul este evitat și, în schimb, unele containere etc. sunt auto-scrise, codul este aproape sigur mai puțin performant și conține mai multe erori. (Lucream la un astfel de sistem la începutul anilor 2000. Era bine încorporat, infotainment, dar avea resursele unui PC cu 5 ani înainte.) - person Peter - Reinstate Monica; 22.01.2020
comment
@Peter-ReinstateMonica vorbim în mod explicit despre std::bitset doar aici. - person Martin York; 22.01.2020
comment
@MartinYork Am încercat să susțin afirmația ta; STL/Boost care nu este utilizat pe sistemele încorporate este, de asemenea, greșit, este corect. Este folosit. (Și, uneori, atunci când sistemul este suficient de capabil și software-ul este suficient de complex, este de fapt o greșeală să nu folosiți STL-ul, deoarece alternativele -- fără generice sau produse la domiciliu -- sunt mai rele. ) - person Peter - Reinstate Monica; 22.01.2020

Cealaltă opțiune este să utilizați câmpuri de biți:

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

struct bits mybits;

definește un câmp de 3 biți (de fapt, sunt trei câmpuri de 1 biți). Operațiile cu biți devin acum puțin (haha) mai simple:

Pentru a seta sau a șterge puțin:

mybits.b = 1;
mybits.c = 0;

Pentru a comuta puțin:

mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1;  /* all work */

Verificand putin:

if (mybits.c)  //if mybits.c is non zero the next line below will execute

Acest lucru funcționează numai cu câmpuri de biți de dimensiune fixă. În caz contrar, trebuie să recurgeți la tehnicile de biți-twiddling descrise în postările anterioare.

person Ferruccio    schedule 11.09.2008
comment
Întotdeauna am găsit că folosirea câmpurilor de biți este o idee proastă. Nu aveți control asupra ordinii în care sunt alocați biții (de sus sau de jos), ceea ce face imposibilă serializarea valorii într-un mod stabil/portabil, cu excepția bit-at-a-time. De asemenea, este imposibil să amestecați aritmetica de biți DIY cu câmpuri de biți, de exemplu, făcând o mască care testează mai mulți biți simultan. Desigur, puteți folosi && și sperați că compilatorul îl va optimiza corect... - person R.. GitHub STOP HELPING ICE; 28.06.2010
comment
Câmpurile de biți sunt proaste în atât de multe feluri, încât aproape că aș putea scrie o carte despre asta. De fapt, aproape că a trebuit să fac asta pentru un program de teren care avea nevoie de conformitate MISRA-C. MISRA-C impune ca toate comportamentele definite de implementare să fie documentate, așa că am ajuns să scriu un eseu despre tot ce poate merge prost în câmpurile de biți. Ordinea biților, endianess, biți de umplutură, octeți de completare, diverse alte probleme de aliniere, conversii implicite și explicite de tip la și de la un câmp de biți, UB dacă int nu este folosit și așa mai departe. În schimb, utilizați operatori pe biți pentru mai puține erori și cod portabil. Câmpurile de biți sunt complet redundante. - person Lundin; 18.08.2011
comment
La fel ca majoritatea caracteristicilor de limbă, câmpurile de biți pot fi utilizate corect sau pot fi abuzate. Dacă trebuie să împachetați mai multe valori mici într-un singur int, câmpurile de biți pot fi foarte utile. Pe de altă parte, dacă începeți să faceți presupuneri cu privire la modul în care câmpurile de biți se mapează cu int care conține efectiv, vă cereți doar probleme. - person Ferruccio; 18.08.2011
comment
Deci, ordinea biților este arbitrară și nu puteți folosi aceasta pentru a schimba biți individuali într-un microcontroler? - person endolith; 08.03.2012
comment
@endolith: Nu ar fi o idee bună. L-ați putea face să funcționeze, dar nu ar fi neapărat portabil la un alt procesor, sau la un alt compilator sau chiar la următoarea ediție a aceluiași compilator. - person Ferruccio; 09.03.2012
comment
Asta e grozav. Întrebarea mea este totuși: după ce verific sizeof(mybits), primesc 12 (adică dimensiunea de trei ints). Acesta este spațiul alocat în memorie sau vreo eroare în funcția sizeof? - person iGbanam; 29.11.2012
comment
@Yasky - ce compilator folosești? Primesc 4 atât cu VC++11, cât și cu Clang 3.1. - person Ferruccio; 29.11.2012
comment
@Ferruccio Folosesc gcc versiunea 4.4.5 - person iGbanam; 29.11.2012
comment
@R. Este posibil să folosiți ambele, struct poate fi introdus într-un union (de obicei anonim) cu un număr întreg etc. Funcționează. (Îmi dau seama că acesta este un thread vechi, btw) - person Shade; 17.05.2014
comment
@Shade Nu există garanții că biții câmpului de biți se mapează într-un mod corect și previzibil cu celelalte tipuri de date din aceeași uniune. Toate aceste coduri vor fi în cel mai bun caz complet neportabile. - person Lundin; 14.12.2015
comment
@Yasky și Ferruccio care obțin răspunsuri diferite la un sizeof() pentru această abordare ar trebui să ilustreze problemele de compatibilitate nu doar între compilatoare, ci și între hardware. Uneori ne păcălim că am rezolvat aceste probleme cu limbi sau timpi de execuție definiti, dar totul se reduce la „va funcționa pe mașina mea?”. Voi băieți încorporați aveți respectul (și simpatiile mele). - person Kelly S. French; 08.12.2016
comment
poate vrei __attribute__((packed)) - person ; 28.06.2020

Folosesc macrocomenzi definite într-un fișier antet pentru a gestiona setarea și ștergerea biților:

/* 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)))
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))
person Community    schedule 04.11.2008
comment
Îmi dau seama că aceasta este o postare veche de 5 ani, dar nu există o dublare a argumentelor în niciuna dintre acele macrocomenzi, Dan - person Robert Kelly; 02.10.2013
comment
BITMASK_CHECK(x,y) ((x) & (y)) trebuie să fie ((x) & (y)) == (y), altfel returnează un rezultat incorect pe mască multibiți (ex. 5 vs. 3) /*Bună ziua tuturor groparilor :)*/ - person brigadir; 11.12.2014
comment
1 ar trebui să fie (uintmax_t)1 sau similar în cazul în care cineva încearcă să folosească aceste macrocomenzi pe un tip long sau mai mare - person M.M; 07.02.2015
comment
Sau 1ULL funcționează la fel de bine ca (uintmax_t) pe majoritatea implementărilor. - person Peter Cordes; 11.11.2017
comment
@brigadir: depinde dacă doriți să verificați dacă există vreun set de biți sau toți biții setați. Am actualizat răspunsul pentru a le include pe ambele cu nume descriptive. - person Peter Cordes; 11.11.2017
comment
BITMASK_CHECK_ALL(x,y) poate fi implementat ca !~((~(y))|(x)) - person Handy999; 20.11.2018
comment
@Handy999 Este puțin mai ușor să vezi de ce funcționează asta după aplicarea legii lui De Morgan și rearanjarea pentru a obține !(~(x) & (y)) - person Tavian Barnes; 13.08.2019
comment
Acest lucru a fost atât de extrem de util încât mă întreb de ce nu am încercat asta până acum, mai ales #define BIT_CHECK(a,b) (!!((a) & (1ULL<<(b)))) // '!!' to make sure this returns 0 or 1 - person William Martens; 16.02.2021

Uneori merită să folosiți un enum pentru a numi biții:

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

Apoi folosiți numele mai târziu. i.e. scrie

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

pentru a seta, a clarifica și a testa. În acest fel, ascundeți numerele magice de restul codului.

În afară de asta, susțin soluția lui Jeremy.

person dmckee --- ex-moderator kitten    schedule 08.09.2008
comment
Alternativ, puteți crea o funcție clearbits() în loc de &= ~. De ce folosești o enumerare pentru asta? Am crezut că acestea sunt pentru a crea o grămadă de variabile unice cu valoare arbitrară ascunsă, dar le atribuiți fiecăreia o valoare definită. Deci, care este beneficiul față de doar definirea lor ca variabile? - person endolith; 20.12.2011
comment
@endolith: Utilizarea lui enums pentru seturi de constante înrudite merge înapoi cu mult în programarea c. Bănuiesc că, cu compilatoarele moderne, singurul avantaj față de const short sau orice altceva este că sunt grupate în mod explicit împreună. Și când le doriți pentru ceva altul decât măștile de biți, obțineți numerotarea automată. În c++, desigur, ele formează și tipuri distincte, ceea ce vă oferă un mic plus de verificare statică a erorilor. - person dmckee --- ex-moderator kitten; 22.12.2011
comment
Veți intra în constante de enumerare nedefinite dacă nu definiți o constantă pentru fiecare dintre valorile posibile ale biților. Care este valoarea enum ThingFlags pentru ThingError|ThingFlag1, de exemplu? - person Luis Colorado; 30.09.2014
comment
Dacă utilizați această metodă, vă rugăm să rețineți că constantele enumerare sunt întotdeauna de tip semnat int. Acest lucru poate cauza tot felul de erori subtile din cauza promovării întregi implicite sau a operațiunilor pe biți pe tipurile semnate. thingstate = ThingFlag1 >> 1 va invoca, de exemplu, comportamentul definit de implementare. thingstate = (ThingFlag1 >> x) << y poate invoca un comportament nedefinit. Și așa mai departe. Pentru a fi în siguranță, aruncați întotdeauna un tip nesemnat. - person Lundin; 14.12.2015
comment
@Lundin: Începând cu C++11, puteți seta tipul de bază al unei enumerații, de exemplu: enum My16Bits: unsigned short { ... }; - person Aiken Drum; 15.03.2016

Din snip-c.zip bitops.h:

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

Bine, hai să analizăm lucrurile...

Expresia comună cu care se pare că aveți probleme în toate acestea este „(1L ‹‹ (posn))”. Tot ceea ce face este să creeze o mască cu un singur bit activat și care va funcționa cu orice tip de întreg. Argumentul „posn” specifică poziția în care doriți bitul. Dacă posn==0, atunci această expresie va fi evaluată la:

0000 0000 0000 0000 0000 0000 0000 0001 binary.

Dacă posn==8, se va evalua:

0000 0000 0000 0000 0000 0001 0000 0000 binary.

Cu alte cuvinte, pur și simplu creează un câmp de 0 cu un 1 la poziția specificată. Singura parte dificilă este în macrocomanda BitClr() unde trebuie să setăm un singur bit 0 într-un câmp de 1. Acest lucru se realizează prin utilizarea complementului 1 al aceleiași expresii notate de operatorul tilde (~).

Odată ce masca este creată, se aplică argumentului așa cum sugerați, prin utilizarea operatorilor biți și (&), sau (|) și xor (^). Deoarece masca este de tip lung, macrocomenzile vor funcționa la fel de bine pe caractere, scurte, int sau lungi.

Concluzia este că aceasta este o soluție generală pentru o întreagă clasă de probleme. Desigur, este posibil și chiar adecvat să rescrieți echivalentul oricăreia dintre aceste macrocomenzi cu valori explicite ale măștii de fiecare dată când aveți nevoie de una, dar de ce o faceți? Amintiți-vă, substituția macro are loc în preprocesor și astfel codul generat va reflecta faptul că valorile sunt considerate constante de către compilator - adică este la fel de eficient să utilizați macrocomenzile generalizate ca să „reinventați roata” de fiecare dată când trebuie să faceți. faceți manipularea biților.

Neconvins? Iată un cod de test - am folosit Watcom C cu optimizare completă și fără a folosi _cdecl, astfel încât dezasamblarea rezultată să fie cât mai curată posibil:

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

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

person yogeesh    schedule 17.09.2008
comment
2 lucruri despre asta: (1) în examinarea macrocomenzilor dvs., unii pot crede în mod incorect că macrocomenzile setează/șteresc/întorc biți în arg, totuși nu există nicio atribuire; (2) testul dumneavoastră.c nu este complet; Bănuiesc că dacă ai rula mai multe cazuri ai găsi o problemă (exercițiu pentru cititor) - person Dan; 18.10.2008
comment
-1 Aceasta este doar o confuzie ciudată. Nu reinventați niciodată limbajul C ascunzând sintaxa limbajului în spatele macrocomenzilor, este o practică foarte proastă. Apoi câteva ciudățeni: mai întâi, 1L este semnat, adică toate operațiunile pe biți vor fi efectuate pe un tip semnat. Tot ceea ce este transmis acestor macrocomenzi va reveni ca fiind semnat lung. Nu e bun. În al doilea rând, acest lucru va funcționa foarte ineficient pe procesoarele mai mici, deoarece se aplică mult timp atunci când operațiunile ar fi putut fi la nivel int. În al treilea rând, macro-urile asemănătoare funcțiilor sunt rădăcina tuturor relelor: nu aveți nicio siguranță de tip. De asemenea, comentariul anterior despre nicio misiune este foarte valid. - person Lundin; 18.08.2011
comment
Acest lucru va eșua dacă arg este long long. 1L trebuie să fie cel mai larg tip posibil, deci (uintmax_t)1 . (S-ar putea să scapi cu 1ull) - person M.M; 07.02.2015
comment
Ați optimizat pentru dimensiunea codului? Pe procesoarele Intel mainstream, veți obține blocări parțiale de registru când citiți AX sau EAX după revenirea acestei funcție, deoarece scrie componentele pe 8 biți ale EAX. (Este bine pe procesoarele AMD sau altele care nu redenumesc registrele parțiale separat de registrul complet. Haswell/Skylake nu redenumesc AL separat, dar redenumesc AH.). - person Peter Cordes; 11.11.2017

Pentru începători aș dori să explic puțin mai multe cu un exemplu:

Exemplu:

value is 0x55;
bitnum : 3rd.

Operatorul & este utilizat verificați bitul:

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)

Comutați sau răsturnați:

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

operator |: setați bitul

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

Deoarece acesta este etichetat „încorporat”, voi presupune că utilizați un microcontroler. Toate sugestiile de mai sus sunt valide și funcționează (citire-modificare-scriere, uniuni, structuri etc.).

Cu toate acestea, în timpul unei crize de depanare bazată pe osciloscop, am fost uimit să descopăr că aceste metode au o supraîncărcare considerabilă în ciclurile CPU, în comparație cu scrierea unei valori direct în registrele PORTnSET / PORTnCLEAR ale micro-ului, ceea ce face o diferență reală acolo unde există bucle strânse / ridicate. - pini de comutare ISR de frecventa.

Pentru cei nefamiliarizați: În exemplul meu, micro are un registru general de stare a pinului PORTn care reflectă pinii de ieșire, astfel încât PORTn |= BIT_TO_SET are ca rezultat o citire-modificare-scriere în acel registru. Cu toate acestea, registrele PORTnSET / PORTnCLEAR iau un „1” pentru a însemna „vă rugăm să faceți acest bit 1” (SET) sau „vă rugăm să faceți acest bit zero” (CLEAR) și un „0” pentru a însemna „lasați pinul în pace”. deci, ajungeți cu două adrese de porturi, în funcție de dacă setați sau ștergeți bitul (nu întotdeauna convenabil), dar o reacție mult mai rapidă și un cod asamblat mai mic.

person John U    schedule 14.06.2012
comment
Micro a fost Coldfire MCF52259, folosind C în Codewarrior. Privirea dezasamblatorului/asm-ului este un exercițiu util, deoarece arată toți pașii pe care trebuie să îi parcurgă procesorul pentru a face chiar și cea mai de bază operație. ‹br›Am observat, de asemenea, alte instrucțiuni de acumulare a procesorului în bucle critice în timp - constrângerea unei variabile făcând var %= max_val costă o grămadă de cicluri CPU de fiecare dată, în timp ce făcând if(var › max_val)var-=max_val folosește numai câteva instrucțiuni. ‹br›Un ghid bun pentru alte câteva trucuri este aici: codeproject.com/Articles/6154/ - person John U; 19.06.2012
comment
Și mai important, registrele I/O mapate în memorie de ajutor oferă un mecanism pentru actualizări atomice. Citirea/modificarea/scrierea poate merge foarte prost dacă secvența este întreruptă. - person Ben Voigt; 22.02.2015
comment
Rețineți că toate registrele de porturi vor fi definite ca volatile și, prin urmare, compilatorul nu poate efectua nicio optimizare a codului care implică astfel de registre. Prin urmare, este o practică bună să dezasamblați un astfel de cod și să vedeți cum a ieșit la nivel de asamblare. - person Lundin; 14.12.2015

Iată macrocomanda mea aritmetică de biți preferată, care funcționează pentru orice tip de matrice de numere întregi fără semn de la unsigned char până la size_t (care este cel mai mare tip cu care ar trebui să funcționeze eficient):

#define BITOP(a,b,op) \
 ((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))

Pentru a seta un pic:

BITOP(array, bit, |=);

Pentru a clarifica un pic:

BITOP(array, bit, &=~);

Pentru a comuta puțin:

BITOP(array, bit, ^=);

Pentru a testa un pic:

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

etc.

person R.. GitHub STOP HELPING ICE    schedule 13.07.2010
comment
Este bine de citit, dar ar trebui să fii conștient de posibilele efecte secundare. Utilizarea BITOP(array, bit++, |=); într-o buclă, cel mai probabil, nu va face ceea ce dorește apelantul. - person foraidt; 13.07.2010
comment
Într-adevăr. =) O variantă pe care o preferați este să o separați în 2 macro-uri, una pentru adresarea elementului de matrice și cealaltă pentru mutarea bitului în loc, ala BITCELL(a,b) |= BITMASK(a,b); (ambele iau a ca argument pentru a determina dimensiunea, dar acesta din urmă nu ar fi niciodată evaluați a deoarece apare doar în sizeof). - person R.. GitHub STOP HELPING ICE; 13.07.2010
comment
@R.. Acest răspuns este foarte vechi, dar probabil că aș prefera o funcție unei macrocomenzi în acest caz. - person PC Luddite; 23.10.2015
comment
Minor: a treia distribuție (size_t) pare să fie acolo doar pentru a asigura unele matematice nesemnate cu %. Ar putea (unsigned) acolo. - person chux - Reinstate Monica; 27.09.2017
comment
(size_t)(b)/(8*sizeof *(a)) s-ar putea îngusta în mod inutil b înainte de diviziune. Doar o problemă cu matricele de biți foarte mari. Încă un macro interesant. - person chux - Reinstate Monica; 27.09.2017

Abordarea bitfield are alte avantaje în arena încorporată. Puteți defini o structură care se mapează direct pe biții dintr-un anumit registru hardware.

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;

Trebuie să fiți conștienți de ordinea de ambalare a biților - cred că este mai întâi MSB, dar acest lucru poate depinde de implementare. De asemenea, verificați modul în care compilatorul dvs. gestionează câmpurile care traversează granițele octeților.

Apoi puteți citi, scrie, testa valorile individuale ca înainte.

person Roddy    schedule 06.11.2008
comment
Aproape totul despre câmpurile de biți este definit de implementare. Chiar dacă reușiți să aflați toate detaliile cu privire la modul în care compilatorul dvs. le implementează, utilizarea lor în codul dvs. va face cu siguranță să nu fie portabil. - person Lundin; 18.08.2011
comment
@Lundin - Adevărat, dar jocul de biți a sistemului încorporat (în special în registrele hardware, la care se referă răspunsul meu) nu va fi oricum portabil în mod util. - person Roddy; 20.08.2011
comment
Poate nu între procesoare complet diferite. Dar cel mai probabil doriți să fie portabil între compilatoare și între diferite proiecte. Și există o mulțime de biți încorporate care nu sunt deloc legate de hardware, cum ar fi codificarea/decodarea protocolului de date. - person Lundin; 20.08.2011
comment
... și dacă vă obișnuiți să utilizați câmpuri de biți pentru programare încorporată, veți descoperi că codul dvs. X86 rulează mai rapid și mai slab. Nu în benchmark-uri simple în care aveți întreaga mașină pentru a zdrobi benchmark-ul, ci în medii multitasking din lumea reală în care programele concurează pentru resurse. Avantaj CISC - al cărui obiectiv inițial de proiectare a fost să compenseze procesoarele mai rapid decât magistralele și memoria lentă. - person ; 16.02.2013

Verificați puțin într-o locație arbitrară într-o variabilă de tip arbitrar:

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

Eșantion de utilizare:

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

Note: acesta este conceput pentru a fi rapid (dată fiind flexibilitatea sa) și fără ramuri. Rezultă un cod de mașină SPARC eficient atunci când este compilat Sun Studio 8; De asemenea, l-am testat folosind MSVC++ 2008 pe amd64. Este posibil să faci macrocomenzi similare pentru setarea și ștergerea biților. Diferența cheie a acestei soluții în comparație cu multe altele de aici este că funcționează pentru orice locație în aproape orice tip de variabilă.

person John Zwinck    schedule 03.01.2009

Mai general, pentru hărți de bit de dimensiuni arbitrare:

#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)))
person bill    schedule 13.06.2009
comment
CHAR_BIT este deja definit de limits.h, nu trebuie să introduceți propriul BITS (și, de fapt, vă înrăutățiți codul făcând asta) - person M.M; 07.02.2015

Acest program este de a schimba orice bit de date de la 0 la 1 sau de la 1 la 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);
        }
    }
}
person Gokul Naathan    schedule 28.02.2012

Foloseste asta:

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

    return num;
}
person Community    schedule 11.04.2009
comment
Ei bine, folosește ramificare ineficientă. - person asdf; 02.07.2011
comment
@asdf Sarcina compilatorului este să scoată cel mai eficient binar, sarcina programatorului este să scrie cod clar - person M.M; 07.02.2015
comment
Aceasta este o bună demonstrație de testare, setare și ștergere a unui anumit bit. Cu toate acestea, este o abordare foarte proastă pentru a comuta puțin. - person Ben Voigt; 22.02.2015

Dacă faci o mulțime de zbârcituri, s-ar putea să vrei să folosești măști care vor face totul mai rapid. Următoarele funcții sunt foarte rapide și sunt încă flexibile (permit răsucirea biților în hărți de biți de orice dimensiune).

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

Rețineți, pentru a seta bitul „n” într-un număr întreg de 16 biți, faceți următoarele:

TSetBit( n, &my_int);

Depinde de dvs. să vă asigurați că numărul de biți se află în intervalul hărții de biți pe care o treceți. Rețineți că, pentru procesoarele Little Endian care octeți, cuvinte, dwords, qwords etc., se mapează corect între ele în memorie (motivul principal pentru care procesoarele Little Endian sunt „mai bune” decât procesoarele Big-Endian, ah, simt că vine un război de flacără pe...).

person Tim Ring    schedule 17.09.2008
comment
Nu utilizați un tabel pentru o funcție care poate fi implementată cu un singur operator. TQuickByteMask[n] este echivalent cu (1‹‹n). De asemenea, să vă scurtați argumentele este o idee foarte proastă. / și % vor fi de fapt o diviziune, nu deplasare biți/biți și, deoarece diviziunea semnată cu o putere de 2 nu poate fi implementată pe biți. Ar trebui să faceți ca argumentul de tip unsigned int! - person R.. GitHub STOP HELPING ICE; 28.06.2010
comment
Ce rost are asta? Face doar codul mai lent și mai greu de citit? Nu văd un singur avantaj cu el. 1u ‹‹ n este mai ușor de citit pentru programatorii C și poate fi tradus într-o singură instrucțiune CPU. Diviziunea dvs., pe de altă parte, va fi tradusă în aproximativ 10 căpușe, sau chiar la fel de rău ca până la 100 de căpușe, în funcție de cât de prost gestionează arhitectura specifică diviziunea. În ceea ce privește caracteristica bitmap, ar fi mai logic să existe un tabel de căutare care să traducă fiecare index de biți într-un index de octeți, pentru a optimiza viteza. - person Lundin; 18.08.2011
comment
În ceea ce privește big/little endian, big endian va mapa numere întregi și date brute (de exemplu șiruri de caractere) în același mod: de la stânga la dreapta msb la lsb de-a lungul întregului bitmap. În timp ce Little Endian va mapa numerele întregi de la stânga la dreapta ca 7-0, 15-8, 23-18, 31-24, dar datele brute sunt încă de la stânga la dreapta msb la lsb. Așadar, cât de puțin endian este mai bun pentru algoritmul tău particular mă depășește complet, se pare că este opusul. - person Lundin; 18.08.2011
comment
@R.. Un tabel poate fi util dacă platforma dvs. nu se poate deplasa eficient, cum ar fi vechiul microcip mcu, dar, desigur, atunci diviziunea în eșantion este absolut ineficientă - person jeb; 18.11.2011

Extinderea răspunsului bitset:

#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;
}
person kendotwill    schedule 08.05.2014

Să presupunem mai întâi câteva lucruri
num = 55 Întregul pentru a efectua operații pe biți (setați, obțineți, ștergeți, comutați).
n = 4 Poziția de biți bazată pe 0 pentru a efectua operațiuni pe biți.

Cum să obții un pic?

  1. Pentru a obține nth bit de num shift dreapta num, n ori. Apoi executați ȘI & pe biți cu 1.
bit = (num >> n) & 1;

Cum funcționează?

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

Cum se stabilesc un pic?

  1. Pentru a seta un anumit bit de număr. Schimbarea la stânga de 1 n ori. Apoi efectuați operația SAU | pe biți cu num.
num |= (1 << n);    // Equivalent to; num = (1 << n) | num;

Cum funcționează?

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

Cum se curata putin?

  1. Schimbarea la stânga 1, de n ori, adică 1 << n.
  2. Efectuați complement pe biți cu rezultatul de mai sus. Astfel încât al n-lea bit devine dezactivat și restul bitului devine setat, adică ~ (1 << n).
  3. În cele din urmă, efectuați operația AND & pe biți cu rezultatul de mai sus și num. Cei trei pași de mai sus împreună pot fi scrisi ca num & (~ (1 << n));

„Pași

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

Cum funcționează?

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

Cum să comuți puțin?

Pentru a comuta puțin, folosim operatorul XOR ^ pe biți. Operatorul XOR pe biți evaluează la 1 dacă bitul corespunzător al ambilor operanzi este diferit, în caz contrar evaluează la 0.

Ceea ce înseamnă să comutăm puțin, trebuie să efectuăm operația XOR cu bitul pe care doriți să îl comutați și 1.

num ^= (1 << n);    // Equivalent to; num = num ^ (1 << n);

Cum funcționează?

  • Dacă bitul de comutat este 0, atunci 0 ^ 1 => 1.
  • Dacă bitul de comutat este 1, atunci 1 ^ 1 => 0.
       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
       0001 0000
     ^ 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)

Lectură recomandată - Exerciții pentru operatori pe biți

person Pankaj Prakash    schedule 10.06.2019
comment
Multumesc pentru explicatia detaliata. Iată linkul pentru problema de practică pentru BIT Magic link - person Chandra Shekhar; 28.12.2019

Visual C 2010 și, probabil, multe alte compilatoare, au suport direct pentru operațiile booleene încorporate. Un bit are două valori posibile, la fel ca un boolean, așa că putem folosi boolean-uri în schimb - chiar dacă ocupă mai mult spațiu decât un singur bit în memorie în această reprezentare. Acest lucru funcționează, chiar și operatorul sizeof() funcționează corect.

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

Așadar, la întrebarea dvs., IsGph[i] =1 sau IsGph[i] =0 facilitează setarea și ștergerea boolurilor.

Pentru a găsi caractere neimprimabile:

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

Rețineți că acest cod nu are nimic „special”. Se tratează puțin ca un număr întreg - ceea ce, din punct de vedere tehnic, este. Un întreg de 1 bit care poate conține 2 valori și numai 2 valori.

Am folosit odată această abordare pentru a găsi înregistrări duplicate de împrumut, unde loan_number era cheia ISAM, folosind numărul de împrumut din 6 cifre ca index în matricea de biți. Frumos de rapid și, după 8 luni, a dovedit că sistemul mainframe de la care primim datele funcționa de fapt defectuos. Simplitatea matricelor de biți face ca încrederea în corectitudinea lor să fie foarte mare - față de o abordare de căutare, de exemplu.

person Community    schedule 30.12.2012
comment
std::bitset este într-adevăr implementat ca biți de majoritatea compilatoarelor - person galinette; 17.11.2014
comment
@galinette, De acord. Fișierul antet #include ‹bitset› este o resursă bună în acest sens. De asemenea, vectorul de clasă specială‹bool› pentru când aveți nevoie de modificarea mărimii vectorului. C++ STL, Ediția a II-a, Nicolai M. Josuttis le acoperă exhaustiv la paginile 650 și, respectiv, 281. C++11 adaugă câteva capabilități noi la std::bitset, de interes special pentru mine este o funcție hash în containere neordonate. Mulțumesc pentru atenție! O să-mi șterg comentariul de crampe cerebrale. Deja destul gunoaie pe web. Nu vreau să adaug la ea. - person ; 18.11.2014
comment
Aceasta utilizează cel puțin un octet întreg de stocare pentru fiecare bool. Poate chiar 4 octeți pentru setările C89 care folosesc int pentru a implementa bool - person M.M; 07.02.2015
comment
@MattMcNabb, ai dreptate. În C++ dimensiunea tipului int necesară implementării unui boolean nu este specificată de standard. Mi-am dat seama că acest răspuns a fost greșit cu ceva timp în urmă, dar am decis să-l las aici, deoarece oamenii se pare că îl consideră util. Pentru cei care doresc să folosească bits, comentariul lui galinette este cel mai util, la fel ca biblioteca mea de biți aici... stackoverflow.com/a/16534995/1899861 - person ; 12.02.2015
comment
@RocketRoy: Probabil că merită să schimbați propoziția care susține că acesta este un exemplu de operații pe biți, atunci. - person Ben Voigt; 22.02.2015

Dacă doriți să efectuați această operațiune cu programarea C în kernel-ul Linux, vă sugerez să utilizați API-urile standard ale nucleului Linux.

Consultați 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

Notă: Aici întreaga operațiune are loc într-un singur pas. Deci, toate acestea sunt garantate a fi atomice chiar și pe computerele SMP și sunt utile pentru a menține coerența între procesoare.

person Jeegar Patel    schedule 27.05.2016

Iată câteva macrocomenzi pe care le folosesc:

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)
person sam msft    schedule 06.02.2015

Cum setați, ștergeți și comutați un singur bit?

Pentru a rezolva o capcană obișnuită de codare atunci când încercați să formați măști:
1 nu este întotdeauna suficient de largă

Ce probleme se întâmplă când number este un tip mai larg decât 1?
x poate fi prea mare pentru schimbarea 1 << x care duce la comportament nedefinit (UB). Chiar dacă x nu este prea mare, ~ s-ar putea să nu răstoarne suficienți biți cei mai semnificativi.

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

Pentru a vă asigura că 1 este suficient de lat:

Codul ar putea folosi 1ull sau pedant (uintmax_t)1 și să lase compilatorul să se optimizeze.

number |= (1ull << x);
number |= ((uintmax_t)1 << x);

Sau difuzare - ceea ce face ca problemele de codificare/revizuire/întreținere să mențină distribuția corectă și actualizată.

number |= (type_of_number)1 << x;

Sau promovați ușor 1 forțând o operație matematică care este cel puțin la fel de largă ca tipul de number.

number |= (number*0 + 1) << x;

Ca și în cazul majorității manipulărilor de biți, este mai bine să lucrați cu tipuri nesemnate decât cu cele semnate

person chux - Reinstate Monica    schedule 27.09.2017
comment
Interesantă privire la o întrebare veche! Nici number |= (type_of_number)1 << x;, nici number |= (number*0 + 1) << x; nu sunt potrivite pentru a seta bitul de semn al unui tip cu semn... De fapt, nici number |= (1ull << x); nu este. Există o modalitate portabilă de a face acest lucru în funcție de poziție? - person chqrlie; 28.09.2017
comment
@chqrlie IMO, cea mai bună modalitate de a evita setarea bitului de semn și de a risca UB sau IDB cu schimburi este să folosești tipuri nesemnate. Codul de schimb semnat foarte portabil este prea complicat pentru a fi acceptabil. - person chux - Reinstate Monica; 28.09.2017

Acest program se bazează pe soluția de mai sus a lui @Jeremy. Dacă cineva dorește să se joace rapid.

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
person Balaji Boggaram Ramanarayan    schedule 22.04.2020

O versiune șablon (introdusă într-un fișier antet) cu suport pentru schimbarea mai multor biți (funcționează pe microcontrolere AVR de altfel):

namespace bit {
  template <typename T1, typename T2>
  constexpr inline T1 bitmask(T2 bit) 
  {return (T1)1 << bit;}
  template <typename T1, typename T3, typename ...T2>
  constexpr inline T1 bitmask(T3 bit, T2 ...bits) 
  {return ((T1)1 << bit) | bitmask<T1>(bits...);}

  /** Set these bits (others retain their state) */
  template <typename T1, typename ...T2>
  constexpr inline void set (T1 &variable, T2 ...bits) 
  {variable |= bitmask<T1>(bits...);}
  /** Set only these bits (others will be cleared) */
  template <typename T1, typename ...T2>
  constexpr inline void setOnly (T1 &variable, T2 ...bits) 
  {variable = bitmask<T1>(bits...);}
  /** Clear these bits (others retain their state) */
  template <typename T1, typename ...T2>
  constexpr inline void clear (T1 &variable, T2 ...bits) 
  {variable &= ~bitmask<T1>(bits...);}
  /** Flip these bits (others retain their state) */
  template <typename T1, typename ...T2>
  constexpr inline void flip (T1 &variable, T2 ...bits) 
  {variable ^= bitmask<T1>(bits...);}
  /** Check if any of these bits are set */
  template <typename T1, typename ...T2>
  constexpr inline bool isAnySet(const T1 &variable, T2 ...bits) 
  {return variable & bitmask<T1>(bits...);}
  /** Check if all these bits are set */
  template <typename T1, typename ...T2>
  constexpr inline bool isSet (const T1 &variable, T2 ...bits) 
  {return ((variable & bitmask<T1>(bits...)) == bitmask<T1>(bits...));}
  /** Check if all these bits are not set */
  template <typename T1, typename ...T2>
  constexpr inline bool isNotSet (const T1 &variable, T2 ...bits) 
  {return ((variable & bitmask<T1>(bits...)) != bitmask<T1>(bits...));}
}

Exemplu de utilizare:

#include <iostream>
#include <bitset> // for console output of binary values

// and include the code above of course

using namespace std;

int main() {
  uint8_t v = 0b1111'1100;
  bit::set(v, 0);
  cout << bitset<8>(v) << endl;

  bit::clear(v, 0,1);
  cout << bitset<8>(v) << endl;

  bit::flip(v, 0,1);
  cout << bitset<8>(v) << endl;

  bit::clear(v, 0,1,2,3,4,5,6,7);
  cout << bitset<8>(v) << endl;

  bit::flip(v, 0,7);
  cout << bitset<8>(v) << endl;
}

BTW: Se dovedește că constexpr și inline nu sunt folosite dacă nu se trimit argumentul de optimizare (de exemplu: -O3) la compilator. Simțiți-vă liber să încercați codul la https://godbolt.org/ și să priviți rezultatul ASM.

person Joakim L. Christiansen    schedule 10.02.2018
comment
Acest cod este spart. (De asemenea, de ce aveți ; după definițiile funcției dvs.?) - person melpomene; 10.02.2018
comment
@melpomene Codul nu este rupt, l-am testat. Vrei să spui că nu se va compila sau că rezultatul este greșit? Despre extraul „;” Nu-mi amintesc, acestea pot fi eliminate într-adevăr. - person Joakim L. Christiansen; 25.02.2018
comment
Mulțumesc că ai observat, trebuia să fie ((variable & bits) == bits) - person Joakim L. Christiansen; 27.02.2018
comment
utilizați std::bitsetîn c++11 - person pqnet; 25.10.2019
comment
Din păcate, nici măcar nu puteți vedea codul fără să derulați lateral, deoarece este atât de proligent. Poate că câteva linii noi ar ajuta? - person Asteroids With Wings; 22.04.2020

Setarea celui de-al n-lea bit la x (valoarea bitului) fără a utiliza -1

Uneori, când nu sunteți sigur la ce va rezulta -1 sau ceva asemănător, poate doriți să setați al n-lea bit fără a utiliza -1:

number = (((number | (1 << n)) ^ (1 << n))) | (x << n);

Explicație: ((number | (1 << n) setează al n-lea bit la 1 (unde | indică SAU pe biți), apoi cu (...) ^ (1 << n) setăm al n-lea bit la 0, iar în final cu (...) | x << n) setăm al n-lea bit care a fost 0, la (valoarea biților) x.

Acest lucru funcționează și în golang.

person Dominic van der Zypen    schedule 06.02.2021
comment
Acest lucru ar putea fi mult mai succint (și probabil mai eficient, cu excepția cazului în care compilatorul vă optimizează soluția) ca (number & ~(1 << n)) | (!!x << n). - person Will Eccles; 08.05.2021

Iată o rutină în C pentru a efectua operațiunile de bază pe biți:

#define INT_BIT (unsigned int) (sizeof(unsigned int) * 8U) //number of bits in unsigned int

int main(void)
{
    
    unsigned int k = 5; //k is the bit position; here it is the 5th bit from the LSb (0th bit)
    
    unsigned int regA = 0x00007C7C; //we perform bitwise operations on regA
    
    regA |= (1U << k);    //Set kth bit
    
    regA &= ~(1U << k);   //Clear kth bit
    
    regA ^= (1U << k);    //Toggle kth bit
    
    regA = (regA << k) | regA >> (INT_BIT - k); //Rotate left by k bits
    
    regA = (regA >> k) | regA << (INT_BIT - k); //Rotate right by k bits

    return 0;   
}

person lckid2004    schedule 17.03.2021

Încercați una dintre aceste funcții în limbajul C pentru a schimba n biți:

char bitfield;

// Start at 0th position

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

Or

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

Or

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;
}
person Vincet    schedule 27.05.2014
comment
value << n poate provoca un comportament nedefinit - person M.M; 07.02.2015
comment
schimbați 1 în 0x1 sau 1UL pentru a evita UB despre care vorbește @M.M - person KPCT; 18.04.2021

person    schedule
comment
Tipul returnat de check_nth_bit poate fi bool. - person Xeverous; 05.05.2020
comment
@Xeverous da, depinde de intenția apelanților - person Sazzad Hissain Khan; 05.05.2020