Utilisez l'opérateur OR au niveau du bit ( |) pour définir un bit.
number |=1UL<< n;
Cela mettra le ne bit de number. ndevrait être zéro, si vous voulez régler le 1bit st et ainsi de suite jusqu'à n-1, si vous voulez régler le nbit bit.
Utilisez 1ULLsi numberest plus large que unsigned long; la promotion de 1UL << nne se produit qu'après avoir évalué 1UL << noù le comportement indéfini se déplace de plus de la largeur de a long. La même chose s'applique à tous les autres exemples.
Effacer un peu
Utilisez l'opérateur AND au niveau du bit ( &) pour effacer un peu.
number &=~(1UL<< n);
Cela effacera le ne bit de number. Vous devez inverser la chaîne de bits avec l'opérateur NOT au niveau du bit ( ~), puis AND.
Basculer un peu
L'opérateur XOR ( ^) peut être utilisé pour basculer un peu.
number ^=1UL<< n;
Cela fera basculer le ne bit de number.
Vérifier un peu
Vous ne l'avez pas demandé, mais je pourrais aussi bien l'ajouter.
Pour vérifier un peu, déplacez le nombre n vers la droite, puis au niveau du bit ET:
bit =(number >> n)&1U;
Cela mettra la valeur du ne bit de numberdans la variable bit.
Modification de la n ième bit à x
La définition du nth bit sur 1ou 0peut être obtenue avec les éléments suivants sur une implémentation C ++ complémentaire à 2:
number ^=(-x ^ number)&(1UL<< n);
Le bit nsera défini si xest 1, et effacé si xest 0. Si xa une autre valeur, vous obtenez des ordures. x = !!xla booléenne à 0 ou 1.
Pour rendre cela indépendant du comportement de négation du complément à 2 (où -1tous les bits sont définis, contrairement à une implémentation C ++ de complément ou de signe / magnitude), utilisez la négation non signée.
number ^=(-(unsignedlong)x ^ number)&(1UL<< n);
ou
unsignedlong newbit =!!x;// Also booleanize to force 0 or 1
number ^=(-newbit ^ number)&(1UL<< n);
C'est généralement une bonne idée d'utiliser des types non signés pour la manipulation de bits portables.
ou
number =(number &~(1UL<< n))|(x << n);
(number & ~(1UL << n))effacera le nbit e et (x << n)mettra le nbit bit sur x.
C'est aussi généralement une bonne idée de ne pas copier / coller de code en général et tant de gens utilisent des macros de préprocesseur (comme la réponse du wiki communautaire plus bas ) ou une sorte d'encapsulation.
Je voudrais noter que sur les plates-formes qui prennent en charge nativement le bit set / clear (par exemple, les microcontrôleurs AVR), les compilateurs traduiront souvent 'myByte | = (1 << x)' en bit set natif / clear instructions chaque fois que x est une constante, ex: (1 << 5), ou const unsigned x = 5.
Aaron
52
bit = nombre & (1 << x); ne mettra pas la valeur du bit x dans bit à moins que bit ait le type _Bool (<stdbool.h>). Sinon, bit = !! (nombre & (1 << x)); sera ..
Chris Young
23
pourquoi ne changez-vous pas le dernierbit = (number >> x) & 1
aaronman
42
1est un intlittéral, qui est signé. Donc, toutes les opérations ici fonctionnent sur des numéros signés, ce qui n'est pas bien défini par les normes. Les normes ne garantissent pas le complément à deux ou le décalage arithmétique, il est donc préférable de l'utiliser 1U.
Siyuan Ren
50
Je préfère number = number & ~(1 << n) | (x << n);changer le nième bit en x.
jiasli
459
Utilisation de la bibliothèque standard C ++: std::bitset<N>.
La version Boost permet un ensemble de bits de taille d'exécution par rapport à un ensemble de bits de taille de compilation standard de bibliothèque .
+1. Non pas que std :: bitset soit utilisable à partir de "C", mais comme l'auteur a tagué sa question avec "C ++", AFAIK, votre réponse est la meilleure ici ... std :: vector <bool> est une autre façon, si on connaît ses avantages et ses inconvénients
paercebal
23
@andrewdotnich: le vecteur <bool> est (malheureusement) une spécialisation qui stocke les valeurs sous forme de bits. Voir gotw.ca/publications/mill09.htm pour plus d'informations ...
Niklas
71
Peut-être que personne ne l'a mentionné parce que cela était étiqueté comme étant intégré. Dans la plupart des systèmes embarqués, vous évitez les STL comme la peste. Et le support de boost est probablement un oiseau très rare à repérer parmi la plupart des compilateurs intégrés.
Lundin
17
@Martin C'est très vrai. Outre les tueurs de performances spécifiques comme STL et les modèles, de nombreux systèmes embarqués évitent même complètement les bibliothèques standard entières, car ils sont si difficiles à vérifier. La plupart de la branche embarquée adopte des normes comme MISRA, qui nécessitent des outils d'analyse de code statique (tous les professionnels du logiciel devraient utiliser de tels outils entre autres, pas seulement des gens embarqués). Généralement, les gens ont mieux à faire que d'exécuter une analyse statique à travers toute la bibliothèque standard - si son code source est même disponible pour eux sur le compilateur spécifique.
Lundin
37
@Lundin: Vos déclarations sont excessivement larges (donc inutile de discuter). Je suis sûr que je peux trouver des situations où elles sont vraies. Cela ne change pas mon point de départ. Ces deux classes sont parfaitement adaptées à une utilisation dans des systèmes embarqués (et je sais pertinemment qu'elles sont utilisées). Votre point initial sur le fait que STL / Boost n'est pas utilisé sur les systèmes embarqués est également faux. Je suis sûr qu'il existe des systèmes qui ne les utilisent pas et même les systèmes qui les utilisent, ils sont utilisés judicieusement, mais dire qu'ils ne sont pas utilisés n'est tout simplement pas correct (car il existe des systèmes où ils sont utilisés).
Martin York
248
L'autre option consiste à utiliser des champs de bits:
définit un champ de 3 bits (en fait, il s'agit de trois champs de 1 bit). Les opérations sur les bits deviennent maintenant un peu (haha) plus simples:
Pour régler ou effacer un peu:
mybits.b =1;
mybits.c =0;
Pour basculer un peu:
mybits.a =!mybits.a;
mybits.b =~mybits.b;
mybits.c ^=1;/* all work */
Vérification un peu:
if(mybits.c)//if mybits.c is non zero the next line below will execute
Cela ne fonctionne qu'avec des champs de bits de taille fixe. Sinon, vous devez recourir aux techniques de twiddling décrites dans les articles précédents.
J'ai toujours trouvé que l'utilisation des champs de bits était une mauvaise idée. Vous n'avez aucun contrôle sur l'ordre dans lequel les bits sont alloués (du haut ou du bas), ce qui rend impossible la sérialisation de la valeur de manière stable / portable, sauf bit par bit. Il est également impossible de mélanger l'arithmétique des bits de bricolage avec les champs de bits, par exemple en créant un masque qui teste plusieurs bits à la fois. Vous pouvez bien sûr utiliser && et espérer que le compilateur l'optimisera correctement ...
R .. GitHub STOP HELPING ICE
34
Les champs de bits sont mauvais à bien des égards, je pourrais presque écrire un livre à ce sujet. En fait, j'ai presque dû le faire pour un programme de terrain peu qui nécessitait la conformité MISRA-C. MISRA-C applique tous les comportements définis par l'implémentation à documenter, j'ai donc fini par écrire un essai sur tout ce qui peut mal se passer dans les champs de bits. Ordre des bits, endianess, bits de remplissage, octets de remplissage, divers autres problèmes d'alignement, conversions de types implicites et explicites vers et depuis un champ de bits, UB si int n'est pas utilisé, etc. Utilisez plutôt des opérateurs au niveau du bit pour moins de bogues et du code portable. Les champs de bits sont complètement redondants.
Lundin
44
Comme la plupart des fonctionnalités linguistiques, les champs de bits peuvent être utilisés correctement ou ils peuvent être utilisés abusivement. Si vous devez regrouper plusieurs petites valeurs dans un seul entier, les champs de bits peuvent être très utiles. D'un autre côté, si vous commencez à faire des hypothèses sur la façon dont les champs de bits sont mappés à l'intent contenant réel, vous demandez simplement des problèmes.
Ferruccio
4
@endolith: Ce ne serait pas une bonne idée. Vous pourriez le faire fonctionner, mais il ne serait pas nécessairement portable sur un autre processeur, sur un autre compilateur ou même sur la prochaine version du même compilateur.
Ferruccio
3
@Yasky et Ferruccio obtenant des réponses différentes à un sizeof () pour cette approche devraient illustrer les problèmes de compatibilité non seulement entre les compilateurs mais aussi avec le matériel. Nous nous trompons parfois que nous avons résolu ces problèmes avec des langages ou des temps d'exécution définis, mais cela se résume vraiment à "cela fonctionnera-t-il sur ma machine?". Vous, les gars intégrés, avez mon respect (et mes sympathies).
Kelly S. French
181
J'utilise des macros définies dans un fichier d'en-tête pour gérer les bits définis et effacer:
/* 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))
Euh, je me rends compte que c'est un article de 5 ans mais il n'y a pas de duplication d'argument dans aucune de ces macros, Dan
Robert Kelly
11
BITMASK_CHECK(x,y) ((x) & (y))doit être ((x) & (y)) == (y)sinon il retourne un résultat incorrect sur le masque multibit (ex. 5vs 3) / * Bonjour à tous les fossoyeurs:) * /
brigadir
7
1devrait être (uintmax_t)1ou similaire dans le cas où quelqu'un essaie d'utiliser ces macros sur un longtype plus grand
MM
2
BITMASK_CHECK_ALL(x,y)peut être implémenté comme!~((~(y))|(x))
Handy999
3
@ Handy999 Il est un peu plus facile de voir pourquoi cela fonctionne après avoir appliqué la loi De Morgan et réorganisé pour obtenir!(~(x) & (y))
Tavian Barnes
114
Il vaut parfois la peine d'utiliser un enumpour nommer les bits:
Alternativement, vous pouvez créer une clearbits()fonction au lieu de &= ~. Pourquoi utilisez-vous une énumération pour cela? Je pensais que c'était pour créer un tas de variables uniques avec une valeur arbitraire cachée, mais vous attribuez une valeur définie à chacune. Alors, quel est l'avantage par rapport à leur simple définition de variables?
endolith
4
@endolith: L'utilisation de enums pour des ensembles de constantes liées remonte très loin dans la programmation c. Je soupçonne qu'avec les compilateurs modernes, le seul avantage par rapport à const shortquoi que ce soit, c'est qu'ils sont explicitement regroupés. Et lorsque vous les voulez pour autre chose que des masques de bit, vous obtenez la numérotation automatique. En c ++ bien sûr, ils forment également des types distincts, ce qui vous permet de vérifier les erreurs statiques en plus.
dmckee --- chaton ex-modérateur
Vous entrerez dans des constantes d'énumération non définies si vous ne définissez pas de constante pour chacune des valeurs possibles des bits. Quelle est la enum ThingFlagsvaleur ThingError|ThingFlag1, par exemple?
Luis Colorado
6
Si vous utilisez cette méthode, n'oubliez pas que les constantes enum sont toujours de type signé int. Cela peut provoquer toutes sortes de bogues subtils en raison de la promotion implicite d'entiers ou d'opérations au niveau du bit sur les types signés. thingstate = ThingFlag1 >> 1invoquera par exemple un comportement défini par l'implémentation. thingstate = (ThingFlag1 >> x) << ypeut invoquer un comportement non défini. Etc. Pour être sûr, toujours cast dans un type non signé.
Lundin
1
@Lundin: Depuis C ++ 11, vous pouvez définir le type sous-jacent d'une énumération, par exemple: enum My16Bits: unsigned short { ... };
/*
** Bit set, clear, and test operations
**
** public domain snippet by Bob Stout
*/typedefenum{ERROR =-1, FALSE, TRUE} LOGICAL;#define BOOL(x)(!(!(x)))#defineBitSet(arg,posn)((arg)|(1L<<(posn)))#defineBitClr(arg,posn)((arg)&~(1L<<(posn)))#defineBitTst(arg,posn) BOOL((arg)&(1L<<(posn)))#defineBitFlp(arg,posn)((arg)^(1L<<(posn)))
OK, analysons les choses ...
L'expression courante avec laquelle vous semblez avoir des problèmes avec tous ces éléments est "(1L << (posn))". Tout cela ne fait que créer un masque avec un seul bit et qui fonctionnera avec n'importe quel type entier. L'argument "posn" spécifie la position où vous voulez que le bit. Si posn == 0, alors cette expression sera évaluée à:
00000000000000000000000000000001 binary.
Si posn == 8, il évaluera:
00000000000000000000000100000000 binary.
En d'autres termes, il crée simplement un champ de 0 avec un 1 à la position spécifiée. La seule partie délicate est dans la macro BitClr () où nous devons définir un seul bit 0 dans un champ de 1. Ceci est accompli en utilisant le complément 1 de la même expression que celle indiquée par l'opérateur tilde (~).
Une fois le masque créé, il est appliqué à l'argument comme vous le suggérez, en utilisant les opérateurs au niveau du bit et (&), ou (|) et xor (^). Puisque le masque est de type long, les macros fonctionneront tout aussi bien sur les caractères, les courts, les int ou les longs.
L'essentiel est qu'il s'agit d'une solution générale à toute une classe de problèmes. Il est bien sûr possible et même approprié de réécrire l'équivalent de n'importe laquelle de ces macros avec des valeurs de masque explicites à chaque fois que vous en avez besoin, mais pourquoi le faire? N'oubliez pas que la substitution de macro se produit dans le préprocesseur et que le code généré reflétera le fait que les valeurs sont considérées comme constantes par le compilateur - c'est-à-dire qu'il est tout aussi efficace d'utiliser les macros généralisées que de "réinventer la roue" à chaque fois que vous en avez besoin. faire de la manipulation de bits.
Pas convaincu? Voici un code de test - j'ai utilisé Watcom C avec une optimisation complète et sans utiliser _cdecl pour que le démontage résultant soit aussi propre que possible:
#define BOOL(x)(!(!(x)))#defineBitSet(arg,posn)((arg)|(1L<<(posn)))#defineBitClr(arg,posn)((arg)&~(1L<<(posn)))#defineBitTst(arg,posn) BOOL((arg)&(1L<<(posn)))#defineBitFlp(arg,posn)((arg)^(1L<<(posn)))int bitmanip(int word){
word =BitSet(word,2);
word =BitSet(word,7);
word =BitClr(word,3);
word =BitFlp(word,9);return word;}
2 choses à ce sujet: (1) en parcourant vos macros, certains peuvent croire à tort que les macros définissent / effacent / inversent les bits dans l'argument, mais il n'y a pas d'affectation; (2) votre test.c n'est pas terminé; Je soupçonne que si vous couriez plus de cas, vous trouveriez un problème (exercice de lecture)
Dan
19
-1 C'est juste de l'obscurcissement bizarre. Ne réinventez jamais le langage C en cachant la syntaxe du langage derrière les macros, c'est une très mauvaise pratique. Ensuite, quelques bizarreries: d'abord, 1L est signé, ce qui signifie que toutes les opérations sur les bits seront effectuées sur un type signé. Tout ce qui est passé à ces macros sera renvoyé comme signé longtemps. Pas bon. Deuxièmement, cela fonctionnera de manière très inefficace sur les CPU plus petits car il s'applique longtemps lorsque les opérations auraient pu être au niveau int. Troisièmement, les macros fonctionnelles sont à l'origine de tous les maux: vous n'avez aucune sécurité de type. En outre, le commentaire précédent sur aucune affectation est très valide.
Lundin
2
Cela échouera si argc'est le cas long long. 1Ldoit être le type le plus large possible, donc (uintmax_t)1. (Vous pourriez vous en tirer 1ull)
MM
Avez-vous optimisé la taille du code? Sur les processeurs Intel traditionnels, vous obtiendrez des blocages de registre partiel lorsque vous lirez AX ou EAX après le retour de cette fonction, car elle écrit les composants 8 bits d'EAX. (C'est très bien sur les processeurs AMD, ou d'autres qui ne renomment pas les registres partiels séparément du registre complet. Haswell / Skylake ne renomment pas AL séparément, mais ils renomment AH. ).
Peter Cordes
37
Utilisez les opérateurs au niveau du bit: &|
Pour définir le dernier bit dans 000b:
foo = foo |001b
Pour archiver le dernier bit foo:
if( foo &001b)....
Pour effacer le dernier bit dans foo:
foo = foo &110b
J'ai utilisé XXXbpour plus de clarté. Vous travaillerez probablement avec la représentation HEX, selon la structure de données dans laquelle vous empaquetez les bits.
Voici ma macro arithmétique de bits préférée, qui fonctionne pour tout type de tableau d'entiers non signés allant unsigned charjusqu'à size_t(qui est le plus grand type qui devrait être efficace pour travailler):
#define BITOP(a,b,op) \
((a)[(size_t)(b)/(8*sizeof*(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof*(a)))))
C'est bon à lire, mais il faut être conscient des effets secondaires possibles. L'utilisation BITOP(array, bit++, |=);en boucle ne fera probablement pas ce que l'appelant veut.
foraidt
En effet. =) Une variante que vous préféreriez peut-être est de le séparer en 2 macros, 1 pour adresser l'élément de tableau et l'autre pour déplacer le bit en place, ala BITCELL(a,b) |= BITMASK(a,b);(les deux prennent acomme argument pour déterminer la taille, mais ce dernier n'évaluerait jamais adepuis il n'apparaît qu'en sizeof).
R .. GitHub STOP HELPING ICE
1
@R .. Cette réponse est vraiment ancienne, mais je préférerais probablement une fonction à une macro dans ce cas.
PC Luddite
Mineure: la 3e (size_t)distribution semble être là uniquement pour assurer des mathématiques non signées avec %. Pourrait (unsigned)là.
chux
Le (size_t)(b)/(8*sizeof *(a))pourrait inutilement rétrécir bavant la division. Seul problème avec les tableaux de bits très volumineux. Encore une macro intéressante.
chux
25
Comme cela est étiqueté «intégré», je suppose que vous utilisez un microcontrôleur. Toutes les suggestions ci-dessus sont valables et fonctionnent (lecture-modification-écriture, unions, structures, etc.).
Cependant, lors d'un débogage basé sur un oscilloscope, j'ai été étonné de constater que ces méthodes ont un surcoût considérable dans les cycles CPU par rapport à l'écriture d'une valeur directement dans les registres PORTnSET / PORTnCLEAR du micro, ce qui fait une réelle différence là où il y a des boucles serrées / hautes broches de basculement de l'ISR de fréquence.
Pour ceux qui ne sont pas familiers: Dans mon exemple, le micro a un registre d'état de broche général PORTn qui reflète les broches de sortie, ce qui fait que PORTn | = BIT_TO_SET entraîne une lecture-modification-écriture dans ce registre. Cependant, les registres PORTnSET / PORTnCLEAR prennent un «1» pour signifier «veuillez mettre ce bit à 1» (SET) ou «veuillez mettre ce bit à zéro» (CLEAR) et un «0» pour signifier «laisser la broche tranquille». ainsi, vous vous retrouvez avec deux adresses de port selon que vous définissez ou effacez le bit (pas toujours pratique) mais une réaction beaucoup plus rapide et un code assemblé plus petit.
Micro était Coldfire MCF52259, utilisant C dans Codewarrior. Regarder le désassembleur / asm est un exercice utile car il montre toutes les étapes que le CPU doit parcourir pour effectuer même les opérations les plus élémentaires. <br> Nous avons également repéré d'autres instructions de contournement du processeur dans les boucles à temps critique - contraindre une variable en faisant var% = max_val coûte une pile de cycles CPU à chaque fois, alors que si (var> max_val) var- = max_val utilise uniquement quelques instructions. <br> Un bon guide pour quelques astuces supplémentaires est ici: codeproject.com/Articles/6154/…
John U
Plus important encore, les registres d'E / S mappés en mémoire auxiliaires fournissent un mécanisme pour les mises à jour atomiques. La lecture / modification / écriture peut très mal se passer si la séquence est interrompue.
Ben Voigt
1
Gardez à l'esprit que tous les registres de port seront définis en tant que volatileet par conséquent le compilateur n'est pas en mesure d'effectuer des optimisations sur le code impliquant ces registres. Par conséquent, il est recommandé de démonter un tel code et de voir comment il s'est avéré au niveau de l'assembleur.
Lundin
24
L'approche par champ de bits présente d'autres avantages dans l'arène intégrée. Vous pouvez définir une structure qui mappe directement sur les bits d'un registre matériel particulier.
structHwRegister{unsignedint errorFlag:1;// one-bit flag fieldunsignedintMode:3;// three-bit mode fieldunsignedintStatusCode:4;// four-bit status code};structHwRegister CR3342_AReg;
Vous devez être conscient de l'ordre de compression des bits - je pense que c'est MSB d'abord, mais cela peut dépendre de l'implémentation. Vérifiez également comment vos gestionnaires de compilateur gèrent les champs en franchissant les limites d'octets.
Vous pouvez ensuite lire, écrire, tester les valeurs individuelles comme précédemment.
Presque tout ce qui concerne les champs binaires est défini par l'implémentation. Même si vous parvenez à trouver tous les détails sur la façon dont votre compilateur les implémente, les utiliser dans votre code le rendra certainement non portable.
Lundin
1
@Lundin - Certes, mais le bidouillage du système embarqué (en particulier dans les registres matériels, ce à quoi ma réponse se rapporte) ne sera jamais utile de toute façon.
Roddy
1
Pas entre des processeurs entièrement différents peut-être. Mais vous voudrez probablement qu'il soit portable entre les compilateurs et entre les différents projets. Et il y a beaucoup de "bit fiddling" intégré qui n'est pas du tout lié au matériel, comme l'encodage / décodage du protocole de données.
Lundin
... et si vous avez l'habitude d'utiliser des champs binaires pour faire de la programmation intégrée, vous constaterez que votre code X86 s'exécute plus rapidement et plus léger aussi. Pas dans des benchmarks simples où vous avez toute la machine pour écraser le benchmark, mais dans des environnements multitâches du monde réel où les programmes se disputent les ressources. Avantage CISC - dont l'objectif de conception original était de compenser les CPU plus rapidement que les bus et la mémoire lente.
20
Vérifiez un peu à un emplacement arbitraire dans une variable de type arbitraire:
int main(void){unsignedchar arr[8]={0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF};for(int ix =0; ix <64;++ix)
printf("bit %d is %d\n", ix, bit_test(arr, ix));return0;}
Remarques:
Ceci est conçu pour être rapide (compte tenu de sa flexibilité) et non ramifié. Il en résulte un code machine SPARC efficace lors de la compilation de Sun Studio 8; Je l'ai également testé en utilisant MSVC ++ 2008 sur amd64. Il est possible de créer des macros similaires pour définir et effacer des bits. La principale différence de cette solution par rapport à beaucoup d'autres ici est qu'elle fonctionne pour n'importe quel emplacement dans à peu près n'importe quel type de variable.
Si vous faites beaucoup de twiddling, vous voudrez peut-être utiliser des masques qui rendront le tout plus rapide. Les fonctions suivantes sont très rapides et toujours flexibles (elles permettent le twiddling de bits dans des bit maps de n'importe quelle taille).
constunsignedcharTQuickByteMask[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,};/** Set bit in any sized bit mask.
*
* @return none
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/voidTSetBit(short bit,unsignedchar*bitmap){short n, x;
x = bit /8;// Index to byte.
n = bit %8;// Specific bit in byte.
bitmap[x]|=TQuickByteMask[n];// Set bit.}/** Reset bit in any sized mask.
*
* @return None
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/voidTResetBit(short bit,unsignedchar*bitmap){short n, x;
x = bit /8;// Index to byte.
n = bit %8;// Specific bit in byte.
bitmap[x]&=(~TQuickByteMask[n]);// Reset bit.}/** Toggle bit in any sized bit mask.
*
* @return none
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/voidTToggleBit(short bit,unsignedchar*bitmap){short n, x;
x = bit /8;// Index to byte.
n = bit %8;// Specific bit in byte.
bitmap[x]^=TQuickByteMask[n];// Toggle bit.}/** Checks specified bit.
*
* @return 1 if bit set else 0.
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/shortTIsBitSet(short bit,constunsignedchar*bitmap){short n, x;
x = bit /8;// Index to byte.
n = bit %8;// Specific bit in byte.// Test bit (logigal AND).if(bitmap[x]&TQuickByteMask[n])return1;return0;}/** Checks specified bit.
*
* @return 1 if bit reset else 0.
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/shortTIsBitReset(short bit,constunsignedchar*bitmap){returnTIsBitSet(bit, bitmap)^1;}/** Count number of bits set in a bitmap.
*
* @return Number of bits set.
*
* @param bitmap - Pointer to bitmap.
* @param size - Bitmap size (in bits).
*
* @note Not very efficient in terms of execution speed. If you are doing
* some computationally intense stuff you may need a more complex
* implementation which would be faster (especially for big bitmaps).
* See (http://graphics.stanford.edu/~seander/bithacks.html).
*/intTCountBits(constunsignedchar*bitmap,int size){int i, count =0;for(i=0; i<size; i++)if(TIsBitSet(i, bitmap))
count++;return count;}
Remarque, pour définir le bit 'n' dans un entier de 16 bits, vous procédez comme suit:
TSetBit( n,&my_int);
C'est à vous de vous assurer que le nombre de bits est dans la plage de la carte binaire que vous passez. Notez que pour les petits processeurs endian qui octets, mots, dwords, qwords, etc., se mappent correctement les uns aux autres en mémoire (raison principale pour laquelle les petits processeurs endian sont `` meilleurs '' que les processeurs big-endian, ah, je sens une guerre des flammes venir sur...).
N'utilisez pas de table pour une fonction qui peut être implémentée avec un seul opérateur. TQuickByteMask [n] est équivalent à (1 << n). De plus, rendre vos arguments courts est une très mauvaise idée. Le / et% sera en fait une division, pas un décalage de bits / au niveau du bit et, parce que la division signée par une puissance de 2 ne peut pas être implémentée au niveau du bit. Vous devez rendre le type d'argument unsigned int!
R .. GitHub STOP HELPING ICE
À quoi ça sert? Cela rend le code plus lent et plus difficile à lire? Je ne vois pas un seul avantage avec ça. 1u << n est plus facile à lire pour les programmeurs C et peut, espérons-le, être traduit en une seule instruction d'horloge du processeur. Votre division, d'autre part, sera traduite en quelque chose autour de 10 ticks, ou même aussi mauvais que jusqu'à 100 ticks, selon la façon dont l'architecture spécifique gère mal la division. Quant à la fonctionnalité bitmap, il serait plus logique d'avoir une table de recherche traduisant chaque index de bits en un index d'octets, pour optimiser la vitesse.
Lundin
2
Comme pour le big / little endian, le big endian mappera les entiers et les données brutes (par exemple des chaînes) de la même manière: de gauche à droite msb à lsb tout au long du bitmap. Bien que le petit endian mappe les entiers de gauche à droite en 7-0, 15-8, 23-18, 31-24, mais les données brutes sont toujours de gauche à droite msb en lsb. Alors, comment peu d'endian est meilleur pour votre algorithme particulier me dépasse complètement, il semble que ce soit le contraire.
Lundin
2
@R .. Un tableau peut être utile si votre plate-forme ne peut pas se déplacer efficacement, comme les anciens microprocesseurs à micropuce, mais bien sûr, la division dans l'échantillon est absolument inefficace
jeb
12
Utilisez ceci:
intToggleNthBit(unsignedchar n,int num ){if(num &(1<< n))
num &=~(1<< n);else
num |=(1<< n);return num;}
@asdf Le travail du compilateur est de produire le binaire le plus efficace, le travail du programmeur est d'écrire du code clair
MM
3
Ceci est une bonne démonstration de test, de réglage et d'effacement d'un bit particulier. Cependant, c'est une très mauvaise approche pour basculer un peu.
set_bit Atomicallyset a bit in memory
clear_bit Clears a bit in memory
change_bit Toggle a bit in memory
test_and_set_bit Set a bit andreturn its old value
test_and_clear_bit Clear a bit andreturn its old value
test_and_change_bit Change a bit andreturn its old value
test_bit Determine whether a bit isset
Remarque: Ici, toute l'opération se déroule en une seule étape. Ainsi, tous ces éléments sont garantis atomiques même sur les ordinateurs SMP et sont utiles pour maintenir la cohérence entre les processeurs.
Visual C 2010, et peut-être de nombreux autres compilateurs, prennent directement en charge les opérations booléennes intégrées. Un bit a deux valeurs possibles, tout comme un booléen, afin que nous puissions utiliser des booléens à la place - même s'ils occupent plus d'espace qu'un seul bit dans mémoire dans cette représentation. Cela fonctionne, même l' sizeof()opérateur fonctionne correctement.
Donc, à votre question, IsGph[i] =1ou IsGph[i] =0simplifiez le réglage et la suppression des bools.
Pour rechercher des caractères non imprimables:
// 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;}}
Notez qu'il n'y a rien de "spécial" dans ce code. Il traite un peu comme un entier - ce qui est techniquement le cas. Un entier de 1 bit pouvant contenir 2 valeurs et 2 valeurs uniquement.
J'ai déjà utilisé cette approche pour trouver des enregistrements de prêt en double, où loan_number était la clé ISAM, en utilisant le numéro de prêt à 6 chiffres comme index dans le tableau de bits. Sauvagement rapide et après 8 mois, a prouvé que le système mainframe dont nous obtenions les données fonctionnait en fait mal. La simplicité des tableaux de bits rend la confiance dans leur exactitude très élevée - par rapport à une approche de recherche par exemple.
std :: bitset est en effet implémenté sous forme de bits par la plupart des compilateurs
galinette
@galinette, d'accord. Le fichier d'en-tête #include <bitset> est une bonne ressource à cet égard. En outre, le vecteur de classe spéciale <bool> indique quand vous devez modifier la taille du vecteur. Le C ++ STL, 2nd Edition, Nicolai M. Josuttis les couvre de manière exhaustive sur les pages 650 et 281 respectivement. C ++ 11 ajoute quelques nouvelles capacités à std :: bitset, un intérêt particulier pour moi est une fonction de hachage dans des conteneurs non ordonnés. Merci pour l'information! Je vais supprimer mon commentaire sur les crampes cérébrales. Déjà assez de déchets sur le web. Je ne veux pas y ajouter.
3
Cela utilise au moins un octet entier de stockage pour chacun bool. Peut-être même 4 octets pour les configurations C89 qui utilisent intpour implémenterbool
MM
@MattMcNabb, vous avez raison. En C ++, la taille du type int nécessaire pour implémenter un booléen n'est pas spécifiée par la norme. J'ai réalisé que cette réponse était erronée il y a quelque temps, mais j'ai décidé de la laisser ici car les gens la trouvent apparemment utile. Pour ceux qui souhaitent utiliser les bits, le commentaire de galinette est le plus utile, tout comme ma bibliothèque de bits ici ... stackoverflow.com/a/16534995/1899861
2
@RocketRoy: Il vaut probablement la peine de changer la phrase qui prétend que c'est un exemple "d'opérations sur les bits".
@Xeverous oui cela dépend de l'intention des appelants
Sazzad Hissain Khan
5
Supposons d'abord quelques éléments num = 55 Entier pour effectuer des opérations au niveau du bit (définir, obtenir, effacer, basculer). n = 4Position de bit basée sur 0 pour effectuer des opérations au niveau du bit.
Comment obtenir un peu?
Pour obtenir le nthbit de décalage à droite num num, nfois. Effectuez ensuite ET au niveau du bit &avec 1.
Effectuez le complément au niveau du bit avec le résultat ci-dessus. Pour que le nième bit devienne non défini et que le reste du bit soit défini, c.-à-d.~ (1 << n) .
Enfin, effectuez l'opération ET au niveau du bit &avec le résultat ci-dessus et num. Les trois étapes ci-dessus peuvent être écrites ensemble num & (~ (1 << n));
num &=(~(1<< n));// Equivalent to; num = num & (~(1 << n));
Pour basculer un peu, nous utilisons XOR au niveau du bit ^ opérateur . L'opérateur XOR au niveau du bit est évalué à 1 si le bit correspondant des deux opérandes est différent, sinon il est évalué à 0.
Cela signifie que pour basculer un peu, nous devons effectuer une opération XOR avec le bit que vous souhaitez basculer et 1.
num ^=(1<< n);// Equivalent to; num = num ^ (1 << n);
Merci pour l'explication détaillée. Voici le lien pour le problème de la pratique pour BIT magique lien
Chandra Shekhar
4
Comment définir, effacer et basculer un seul bit?
Pour résoudre un problème de codage courant lors de la tentative de formation du masque: 1n'est pas toujours suffisamment large
Quels problèmes se produisent quand numberest un type plus large que 1? xpeut être trop important pour le changement 1 << xconduisant à un comportement indéfini (UB). Même s'il xn'est pas trop grand, il ~peut ne pas retourner suffisamment de bits de poids fort.
// assume 32 bit int/unsignedunsignedlonglong number = foo();unsigned x =40;
number |=(1<< x);// UB
number ^=(1<< x);// UB
number &=~(1<< x);// UB
x =10;
number &=~(1<< x);// Wrong mask, not wide enough
Pour assurer que 1 est suffisamment large:
Le code pourrait utiliser 1ullou pédantiquement (uintmax_t)1et laisser le compilateur optimiser.
number |=(1ull<< x);
number |=((uintmax_t)1<< x);
Ou cast - ce qui rend les problèmes de codage / révision / maintenance en gardant le cast correct et à jour.
number |=(type_of_number)1<< x;
Ou promouvez doucement le 1en forçant une opération mathématique au moins aussi large que le type de number.
number |=(number*0+1)<< x;
Comme avec la plupart des manipulations de bits, à mieux travailler avec non signés types plutôt que signés les
Regard intéressant sur une vieille question! Ni approprié number |= (type_of_number)1 << x;ni number |= (number*0 + 1) << x;approprié pour définir le bit de signe d'un type signé ... En fait, ni l'un ni l'autre number |= (1ull << x);. Existe-t-il un moyen portable de le faire par position?
chqrlie
1
@chqrlie IMO, la meilleure façon d'éviter de définir le bit de signe et de risquer UB ou IDB avec des décalages est d'utiliser des types non signés . Le code signé par déplacement hautement portable est trop compliqué pour être acceptable.
chux
3
Une version basée sur un modèle C ++ 11 (placée dans un en-tête):
Ce code est cassé. (Aussi, pourquoi avez-vous ;après vos définitions de fonction?)
melpomene
@melpomene Le code n'est pas cassé, je l'ai testé. Voulez-vous dire qu'il ne se compilera pas ou que le résultat est incorrect? À propos de l'extra ';' Je ne me souviens pas, ceux-ci peuvent être supprimés en effet.
Réponses:
Mettre un peu
Utilisez l'opérateur OR au niveau du bit (
|
) pour définir un bit.Cela mettra le
n
e bit denumber
.n
devrait être zéro, si vous voulez régler le1
bit st et ainsi de suite jusqu'àn-1
, si vous voulez régler len
bit bit.Utilisez
1ULL
sinumber
est plus large queunsigned long
; la promotion de1UL << n
ne se produit qu'après avoir évalué1UL << n
où le comportement indéfini se déplace de plus de la largeur de along
. La même chose s'applique à tous les autres exemples.Effacer un peu
Utilisez l'opérateur AND au niveau du bit (
&
) pour effacer un peu.Cela effacera le
n
e bit denumber
. Vous devez inverser la chaîne de bits avec l'opérateur NOT au niveau du bit (~
), puis AND.Basculer un peu
L'opérateur XOR (
^
) peut être utilisé pour basculer un peu.Cela fera basculer le
n
e bit denumber
.Vérifier un peu
Vous ne l'avez pas demandé, mais je pourrais aussi bien l'ajouter.
Pour vérifier un peu, déplacez le nombre n vers la droite, puis au niveau du bit ET:
Cela mettra la valeur du
n
e bit denumber
dans la variablebit
.Modification de la n ième bit à x
La définition du
n
th bit sur1
ou0
peut être obtenue avec les éléments suivants sur une implémentation C ++ complémentaire à 2:Le bit
n
sera défini six
est1
, et effacé six
est0
. Six
a une autre valeur, vous obtenez des ordures.x = !!x
la booléenne à 0 ou 1.Pour rendre cela indépendant du comportement de négation du complément à 2 (où
-1
tous les bits sont définis, contrairement à une implémentation C ++ de complément ou de signe / magnitude), utilisez la négation non signée.ou
C'est généralement une bonne idée d'utiliser des types non signés pour la manipulation de bits portables.
ou
(number & ~(1UL << n))
effacera len
bit e et(x << n)
mettra len
bit bit surx
.C'est aussi généralement une bonne idée de ne pas copier / coller de code en général et tant de gens utilisent des macros de préprocesseur (comme la réponse du wiki communautaire plus bas ) ou une sorte d'encapsulation.
la source
bit = (number >> x) & 1
1
est unint
littéral, qui est signé. Donc, toutes les opérations ici fonctionnent sur des numéros signés, ce qui n'est pas bien défini par les normes. Les normes ne garantissent pas le complément à deux ou le décalage arithmétique, il est donc préférable de l'utiliser1U
.number = number & ~(1 << n) | (x << n);
changer le nième bit en x.Utilisation de la bibliothèque standard C ++:
std::bitset<N>
.Ou la Boost version:
boost::dynamic_bitset
.Il n'est pas nécessaire de rouler le vôtre:
La version Boost permet un ensemble de bits de taille d'exécution par rapport à un ensemble de bits de taille de compilation standard de bibliothèque .
la source
L'autre option consiste à utiliser des champs de bits:
définit un champ de 3 bits (en fait, il s'agit de trois champs de 1 bit). Les opérations sur les bits deviennent maintenant un peu (haha) plus simples:
Pour régler ou effacer un peu:
Pour basculer un peu:
Vérification un peu:
Cela ne fonctionne qu'avec des champs de bits de taille fixe. Sinon, vous devez recourir aux techniques de twiddling décrites dans les articles précédents.
la source
J'utilise des macros définies dans un fichier d'en-tête pour gérer les bits définis et effacer:
la source
BITMASK_CHECK(x,y) ((x) & (y))
doit être((x) & (y)) == (y)
sinon il retourne un résultat incorrect sur le masque multibit (ex.5
vs3
) / * Bonjour à tous les fossoyeurs:) * /1
devrait être(uintmax_t)1
ou similaire dans le cas où quelqu'un essaie d'utiliser ces macros sur unlong
type plus grandBITMASK_CHECK_ALL(x,y)
peut être implémenté comme!~((~(y))|(x))
!(~(x) & (y))
Il vaut parfois la peine d'utiliser un
enum
pour nommer les bits:Ensuite, utilisez les noms plus tard. C'est à dire écrire
pour régler, effacer et tester. De cette façon, vous masquez les nombres magiques du reste de votre code.
À part cela, j'approuve la solution de Jeremy.
la source
clearbits()
fonction au lieu de&= ~
. Pourquoi utilisez-vous une énumération pour cela? Je pensais que c'était pour créer un tas de variables uniques avec une valeur arbitraire cachée, mais vous attribuez une valeur définie à chacune. Alors, quel est l'avantage par rapport à leur simple définition de variables?enum
s pour des ensembles de constantes liées remonte très loin dans la programmation c. Je soupçonne qu'avec les compilateurs modernes, le seul avantage par rapport àconst short
quoi que ce soit, c'est qu'ils sont explicitement regroupés. Et lorsque vous les voulez pour autre chose que des masques de bit, vous obtenez la numérotation automatique. En c ++ bien sûr, ils forment également des types distincts, ce qui vous permet de vérifier les erreurs statiques en plus.enum ThingFlags
valeurThingError|ThingFlag1
, par exemple?int
. Cela peut provoquer toutes sortes de bogues subtils en raison de la promotion implicite d'entiers ou d'opérations au niveau du bit sur les types signés.thingstate = ThingFlag1 >> 1
invoquera par exemple un comportement défini par l'implémentation.thingstate = (ThingFlag1 >> x) << y
peut invoquer un comportement non défini. Etc. Pour être sûr, toujours cast dans un type non signé.enum My16Bits: unsigned short { ... };
Du bitops.h de snip-c.zip:
OK, analysons les choses ...
L'expression courante avec laquelle vous semblez avoir des problèmes avec tous ces éléments est "(1L << (posn))". Tout cela ne fait que créer un masque avec un seul bit et qui fonctionnera avec n'importe quel type entier. L'argument "posn" spécifie la position où vous voulez que le bit. Si posn == 0, alors cette expression sera évaluée à:
Si posn == 8, il évaluera:
En d'autres termes, il crée simplement un champ de 0 avec un 1 à la position spécifiée. La seule partie délicate est dans la macro BitClr () où nous devons définir un seul bit 0 dans un champ de 1. Ceci est accompli en utilisant le complément 1 de la même expression que celle indiquée par l'opérateur tilde (~).
Une fois le masque créé, il est appliqué à l'argument comme vous le suggérez, en utilisant les opérateurs au niveau du bit et (&), ou (|) et xor (^). Puisque le masque est de type long, les macros fonctionneront tout aussi bien sur les caractères, les courts, les int ou les longs.
L'essentiel est qu'il s'agit d'une solution générale à toute une classe de problèmes. Il est bien sûr possible et même approprié de réécrire l'équivalent de n'importe laquelle de ces macros avec des valeurs de masque explicites à chaque fois que vous en avez besoin, mais pourquoi le faire? N'oubliez pas que la substitution de macro se produit dans le préprocesseur et que le code généré reflétera le fait que les valeurs sont considérées comme constantes par le compilateur - c'est-à-dire qu'il est tout aussi efficace d'utiliser les macros généralisées que de "réinventer la roue" à chaque fois que vous en avez besoin. faire de la manipulation de bits.
Pas convaincu? Voici un code de test - j'ai utilisé Watcom C avec une optimisation complète et sans utiliser _cdecl pour que le démontage résultant soit aussi propre que possible:
---- [TEST.C] ----------------------------------------- -----------------------
---- [TEST.OUT (démonté)] -------------------------------------- ---------
---- [finis] ------------------------------------------- ----------------------
la source
arg
c'est le caslong long
.1L
doit être le type le plus large possible, donc(uintmax_t)1
. (Vous pourriez vous en tirer1ull
)Utilisez les opérateurs au niveau du bit:
&
|
Pour définir le dernier bit dans
000b
:Pour archiver le dernier bit
foo
:Pour effacer le dernier bit dans
foo
:J'ai utilisé
XXXb
pour plus de clarté. Vous travaillerez probablement avec la représentation HEX, selon la structure de données dans laquelle vous empaquetez les bits.la source
foo = foo ^ MY_MASK
foo = foo & ~MY_MASK
Pour le débutant, je voudrais expliquer un peu plus avec un exemple:
Exemple:
L'
&
opérateur est utilisé vérifier le bit:Basculer ou retourner:
|
opérateur: définir le bitla source
Voici ma macro arithmétique de bits préférée, qui fonctionne pour tout type de tableau d'entiers non signés allant
unsigned char
jusqu'àsize_t
(qui est le plus grand type qui devrait être efficace pour travailler):Pour régler un peu:
Pour effacer un peu:
Pour basculer un peu:
Pour tester un peu:
etc.
la source
BITOP(array, bit++, |=);
en boucle ne fera probablement pas ce que l'appelant veut.BITCELL(a,b) |= BITMASK(a,b);
(les deux prennenta
comme argument pour déterminer la taille, mais ce dernier n'évaluerait jamaisa
depuis il n'apparaît qu'ensizeof
).(size_t)
distribution semble être là uniquement pour assurer des mathématiques non signées avec%
. Pourrait(unsigned)
là.(size_t)(b)/(8*sizeof *(a))
pourrait inutilement rétrécirb
avant la division. Seul problème avec les tableaux de bits très volumineux. Encore une macro intéressante.Comme cela est étiqueté «intégré», je suppose que vous utilisez un microcontrôleur. Toutes les suggestions ci-dessus sont valables et fonctionnent (lecture-modification-écriture, unions, structures, etc.).
Cependant, lors d'un débogage basé sur un oscilloscope, j'ai été étonné de constater que ces méthodes ont un surcoût considérable dans les cycles CPU par rapport à l'écriture d'une valeur directement dans les registres PORTnSET / PORTnCLEAR du micro, ce qui fait une réelle différence là où il y a des boucles serrées / hautes broches de basculement de l'ISR de fréquence.
Pour ceux qui ne sont pas familiers: Dans mon exemple, le micro a un registre d'état de broche général PORTn qui reflète les broches de sortie, ce qui fait que PORTn | = BIT_TO_SET entraîne une lecture-modification-écriture dans ce registre. Cependant, les registres PORTnSET / PORTnCLEAR prennent un «1» pour signifier «veuillez mettre ce bit à 1» (SET) ou «veuillez mettre ce bit à zéro» (CLEAR) et un «0» pour signifier «laisser la broche tranquille». ainsi, vous vous retrouvez avec deux adresses de port selon que vous définissez ou effacez le bit (pas toujours pratique) mais une réaction beaucoup plus rapide et un code assemblé plus petit.
la source
volatile
et par conséquent le compilateur n'est pas en mesure d'effectuer des optimisations sur le code impliquant ces registres. Par conséquent, il est recommandé de démonter un tel code et de voir comment il s'est avéré au niveau de l'assembleur.L'approche par champ de bits présente d'autres avantages dans l'arène intégrée. Vous pouvez définir une structure qui mappe directement sur les bits d'un registre matériel particulier.
Vous devez être conscient de l'ordre de compression des bits - je pense que c'est MSB d'abord, mais cela peut dépendre de l'implémentation. Vérifiez également comment vos gestionnaires de compilateur gèrent les champs en franchissant les limites d'octets.
Vous pouvez ensuite lire, écrire, tester les valeurs individuelles comme précédemment.
la source
Vérifiez un peu à un emplacement arbitraire dans une variable de type arbitraire:
Exemple d'utilisation:
Remarques: Ceci est conçu pour être rapide (compte tenu de sa flexibilité) et non ramifié. Il en résulte un code machine SPARC efficace lors de la compilation de Sun Studio 8; Je l'ai également testé en utilisant MSVC ++ 2008 sur amd64. Il est possible de créer des macros similaires pour définir et effacer des bits. La principale différence de cette solution par rapport à beaucoup d'autres ici est qu'elle fonctionne pour n'importe quel emplacement dans à peu près n'importe quel type de variable.
la source
Plus général, pour les bitmaps de taille arbitraire:
la source
CHAR_BIT
est déjà défini parlimits.h
, vous n'avez pas besoin de mettre le vôtreBITS
(et en fait vous aggravez votre code en le faisant)Ce programme consiste à changer tout bit de données de 0 à 1 ou de 1 à 0:
la source
Si vous faites beaucoup de twiddling, vous voudrez peut-être utiliser des masques qui rendront le tout plus rapide. Les fonctions suivantes sont très rapides et toujours flexibles (elles permettent le twiddling de bits dans des bit maps de n'importe quelle taille).
Remarque, pour définir le bit 'n' dans un entier de 16 bits, vous procédez comme suit:
C'est à vous de vous assurer que le nombre de bits est dans la plage de la carte binaire que vous passez. Notez que pour les petits processeurs endian qui octets, mots, dwords, qwords, etc., se mappent correctement les uns aux autres en mémoire (raison principale pour laquelle les petits processeurs endian sont `` meilleurs '' que les processeurs big-endian, ah, je sens une guerre des flammes venir sur...).
la source
Utilisez ceci:
la source
Élargir la
bitset
réponse:la source
Si vous souhaitez effectuer toutes ces opérations avec la programmation C dans le noyau Linux, je suggère d'utiliser les API standard du noyau Linux.
Voir https://www.kernel.org/doc/htmldocs/kernel-api/ch02s03.html
Remarque: Ici, toute l'opération se déroule en une seule étape. Ainsi, tous ces éléments sont garantis atomiques même sur les ordinateurs SMP et sont utiles pour maintenir la cohérence entre les processeurs.
la source
Visual C 2010, et peut-être de nombreux autres compilateurs, prennent directement en charge les opérations booléennes intégrées. Un bit a deux valeurs possibles, tout comme un booléen, afin que nous puissions utiliser des booléens à la place - même s'ils occupent plus d'espace qu'un seul bit dans mémoire dans cette représentation. Cela fonctionne, même l'
sizeof()
opérateur fonctionne correctement.Donc, à votre question,
IsGph[i] =1
ouIsGph[i] =0
simplifiez le réglage et la suppression des bools.Pour rechercher des caractères non imprimables:
Notez qu'il n'y a rien de "spécial" dans ce code. Il traite un peu comme un entier - ce qui est techniquement le cas. Un entier de 1 bit pouvant contenir 2 valeurs et 2 valeurs uniquement.
J'ai déjà utilisé cette approche pour trouver des enregistrements de prêt en double, où loan_number était la clé ISAM, en utilisant le numéro de prêt à 6 chiffres comme index dans le tableau de bits. Sauvagement rapide et après 8 mois, a prouvé que le système mainframe dont nous obtenions les données fonctionnait en fait mal. La simplicité des tableaux de bits rend la confiance dans leur exactitude très élevée - par rapport à une approche de recherche par exemple.
la source
bool
. Peut-être même 4 octets pour les configurations C89 qui utilisentint
pour implémenterbool
Utilisez l'un des opérateurs définis ici .
Pour définir un bit, utilisé
int x = x | 0x?;
où?
est la position du bit sous forme binaire.la source
0x
est le préfixe d'un littéral en hexadécimal, pas binaire.Voici quelques macros que j'utilise:
la source
Variable utilisée
value - Data
pos - position du bit que nous souhaitons régler, effacer ou basculer.
Réglez un peu:
Clair un peu:
Basculez un peu:
la source
la source
check_nth_bit
peut êtrebool
.Comment obtenir un peu?
nth
bit de décalage à droite numnum
,n
fois. Effectuez ensuite ET au niveau du bit&
avec 1.Comment ça fonctionne?
Comment régler un peu?
n
fois. Effectuez ensuite l'opération OR au niveau du bit|
avecnum
.Comment ça fonctionne?
Comment effacer un peu?
n
fois ie1 << n
.~ (1 << n)
.&
avec le résultat ci-dessus etnum
. Les trois étapes ci-dessus peuvent être écrites ensemblenum & (~ (1 << n))
;Comment ça fonctionne?
Comment basculer un peu?
Pour basculer un peu, nous utilisons XOR au niveau du bit
^
opérateur . L'opérateur XOR au niveau du bit est évalué à 1 si le bit correspondant des deux opérandes est différent, sinon il est évalué à 0.Cela signifie que pour basculer un peu, nous devons effectuer une opération XOR avec le bit que vous souhaitez basculer et 1.
Comment ça fonctionne?
0 ^ 1 => 1
.1 ^ 1 => 0
.Lecture recommandée - Exercices d'opérateur au niveau du bit
la source
Pour résoudre un problème de codage courant lors de la tentative de formation du masque:
1
n'est pas toujours suffisamment largeQuels problèmes se produisent quand
number
est un type plus large que1
?x
peut être trop important pour le changement1 << x
conduisant à un comportement indéfini (UB). Même s'ilx
n'est pas trop grand, il~
peut ne pas retourner suffisamment de bits de poids fort.Pour assurer que 1 est suffisamment large:
Le code pourrait utiliser
1ull
ou pédantiquement(uintmax_t)1
et laisser le compilateur optimiser.Ou cast - ce qui rend les problèmes de codage / révision / maintenance en gardant le cast correct et à jour.
Ou promouvez doucement le
1
en forçant une opération mathématique au moins aussi large que le type denumber
.Comme avec la plupart des manipulations de bits, à mieux travailler avec non signés types plutôt que signés les
la source
number |= (type_of_number)1 << x;
ninumber |= (number*0 + 1) << x;
approprié pour définir le bit de signe d'un type signé ... En fait, ni l'un ni l'autrenumber |= (1ull << x);
. Existe-t-il un moyen portable de le faire par position?Une version basée sur un modèle C ++ 11 (placée dans un en-tête):
la source
;
après vos définitions de fonction?)(variable & bits == bits)
?((variable & bits) == bits)
std::bitset
en c ++ 11Ce programme est basé sur la solution ci-dessus de @ Jeremy. Si quelqu'un souhaite jouer rapidement.
la source
Essayez l'une de ces fonctions en langage C pour changer n bit:
Ou
Ou
la source
value << n
peut provoquer un comportement indéfini