Opérations bit à bit : AND, OR, XOR, décalages et masques expliqués
Vous ouvrez une ancienne migration PostgreSQL et tombez sur permissions & 0b100. Un collègue livre un système de feature flags qui empaquette 32 booléens dans un seul entier. Un calcul de sous-réseau Kubernetes affiche 192.168.1.0/24 et il faut extraire l’adresse réseau en code. Trois situations, une même compétence sous-jacente : les opérations bit à bit.
La plupart des développeurs applicatifs n’ont jamais besoin de toucher à & ou ^ dans une webapp, jusqu’au jour où si. Ce guide couvre les six opérateurs bit à bit, le complément à deux, neuf motifs à retenir, et les pièges propres à chaque langage (surtout en JavaScript). Le code est en JS, Python, Go et C ; chaque exemple est exécutable.
Ouvrez notre Convertisseur de base dans un autre onglet : plusieurs sections vous invitent à saisir un nombre et regarder le motif binaire changer.
Pourquoi les opérations bit à bit comptent encore en 2026
Les langages de haut niveau n’ont pas rendu les opérations bit à bit obsolètes. Ils les ont juste cachées. Vous en dépendez aujourd’hui sans forcément le savoir :
- Le Row-Level Security de PostgreSQL stocke les privilèges ACL (
SELECT,INSERT,UPDATE,DELETE…) dans un bitmap entier. - Les capabilities Linux remplacent le vieux modèle tout-ou-rien par 40+ bits de permission combinés avec
|. - Les en-têtes d’algorithme JWT encodent le hash dans un petit champ où la comparaison au niveau du bit est courante côté bibliothèque.
- Snowflake, ULID et UUIDv7 empaquètent horodatage, ID machine et numéro de séquence dans un entier 64 ou 128 bits via des décalages à gauche.
BITCOUNTetBITOPde Redis exposent les primitives bit à bit directement à l’application, pour l’estimation de cardinalité et le bucketing A/B.- Le traitement d’image lit les pixels RGBA 32 bits et extrait les canaux avec
&et>>.
Les opérations bit à bit restent du O(1) au niveau instruction CPU. Empaqueter 32 booléens dans un entier économise 31 octets de mémoire et, surtout, permet de tester « l’un de ces 32 flags est-il activé » en une seule comparaison != 0.
Les fondations binaires à maîtriser
Ce guide suppose que vous connaissez déjà le binaire. Si besoin, relisez d’abord notre Guide de conversion de bases numériques puis revenez.
Petit vocabulaire à fixer :
- Un bit vaut 0 ou 1.
- Un nibble fait 4 bits (un chiffre hexadécimal).
- Un octet fait 8 bits.
- Un mot fait typiquement 32 ou 64 bits selon le CPU.
Dans la plupart des langages, les entiers ont une largeur fixe : 8, 16, 32 ou 64. La largeur compte beaucoup pour les opérations bit à bit, parce que les décalages éjectent les bits qui débordent et que le bit de signe occupe la position la plus à gauche des entiers signés.
À essayer tout de suite : ouvrez le Convertisseur de base, saisissez 170 en décimal et regardez la sortie binaire. Vous obtenez 10101010, un motif alterné qu’on recroisera plusieurs fois.
Les six opérateurs bit à bit
Tous les langages mainstream fournissent les six mêmes opérateurs, avec quelques variations syntaxiques. &, |, ^, ~, << et >> fonctionnent à l’identique en JavaScript, Python, Go, Rust, C, C++, Java et C#. JavaScript ajoute >>>, le décalage à droite non signé.
AND (&) : filtre de bits
Le bit résultant vaut 1 uniquement si les deux bits d’entrée valent 1.
| A | B | A & B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
Voyez AND comme une porte : seuls les bits activés des deux côtés passent. L’usage le plus fréquent est le masquage : garder certains bits, annuler les autres.
// Extraire les 4 bits bas (nibble de droite)
const value = 0b11010110; // 214
const low4 = value & 0x0F; // 0b00000110 = 6
// Tester la parité
const isOdd = (n) => (n & 1) === 1;
isOdd(7); // true
isOdd(42); // false
# Identique en Python
value = 0b11010110
low4 = value & 0x0F # 6
def is_odd(n):
return (n & 1) == 1
OR (|) : activateur de bits
Le bit résultant vaut 1 si au moins un des bits d’entrée vaut 1.
| A | B | A | B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
OR combine les drapeaux. Si READ = 1, WRITE = 2 et EXECUTE = 4, alors READ | WRITE vaut 3 : les deux permissions activées.
const READ = 0b001;
const WRITE = 0b010;
const EXEC = 0b100;
const rw = READ | WRITE; // 0b011 = 3
READ, WRITE, EXEC = 0b001, 0b010, 0b100
rw = READ | WRITE # 3
XOR (^) : basculeur de bits
Le bit résultant vaut 1 si les bits d’entrée diffèrent.
| A | B | A ^ B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
XOR a trois propriétés algébriques derrière la plupart de ses astuces :
a ^ a = 0: tout XOR avec lui-même s’annule.a ^ 0 = a: XOR avec zéro est l’identité.a ^ b ^ a = b: XOR est son propre inverse.
La dernière propriété explique pourquoi on retrouve XOR un peu partout, du contrôle de parité aux chiffrements de flux, en passant par le classique problème d’entretien « trouver le nombre unique dans un tableau où tous les autres apparaissent en double ».
// Trouver l'unique dans un tableau où tous les autres apparaissent deux fois
const findUnique = (arr) => arr.reduce((a, b) => a ^ b, 0);
findUnique([4, 1, 2, 1, 2]); // 4
from functools import reduce
from operator import xor
find_unique = lambda arr: reduce(xor, arr, 0)
find_unique([4, 1, 2, 1, 2]) # 4
NOT (~) : inverseur de bits
L’unaire ~ inverse chaque bit : 0 devient 1, 1 devient 0.
~0b00001111 // -16 (JavaScript force 32 bits signés)
~5 // -6
~5 # -6
// Attention : en Go, ^ sert aussi de NOT bit à bit unaire
var x int8 = 5
fmt.Println(^x) // -6
Le résultat ~5 = -6 surprend les débutants dans presque tous les langages. La raison est le complément à deux, traité juste après. Pour l’instant, retenez ceci : ~x vaut toujours -(x + 1) dans tout langage utilisant le complément à deux pour les négatifs, c’est-à-dire tous.
Décalage à gauche (<<) : multiplication par puissance de 2
x << n décale tous les bits de x de n positions vers la gauche, en remplissant de zéros à droite. Mathématiquement, cela multiplie par 2ⁿ.
1 << 0 // 1 (2^0)
1 << 1 // 2 (2^1)
1 << 3 // 8 (2^3)
1 << 10 // 1024 (2^10 = 1 KiB)
// Construire des flags
const FLAG_ADMIN = 1 << 0;
const FLAG_EDITOR = 1 << 1;
const FLAG_REVIEWER = 1 << 2;
L’intérêt de 1 << n : produire un nombre avec un seul bit activé en position n. Ce bit devient un drapeau.
Attention au dépassement. En JavaScript, 1 << 31 vaut -2147483648 (pas 2147483648), parce que les opérations bit à bit JS travaillent sur des entiers 32 bits signés.
Décalage à droite (>> vs >>>) : signé ou rempli de zéros ?
Le décalage à droite pousse les bits vers la droite. Reste à savoir ce qu’on met à la place à gauche.
>>(décalage arithmétique à droite) : préserve le bit de signe. Les négatifs restent négatifs.>>>(décalage logique, non signé) : remplit de zéros. JavaScript est le seul langage courant à avoir cet opérateur dédié.
-8 >> 1 // -4 (signe préservé)
-8 >>> 1 // 2147483644 (signe traité comme bit de donnée)
8 >> 1 // 4
8 >> 2 // 2
En C, pour les types signés, le comportement de >> (arithmétique ou logique) dépend de l’implémentation. La plupart des compilateurs font de l’arithmétique, mais ne vous appuyez pas dessus sans vérifier. Go exige que le décalage soit un entier non signé et distingue strictement signé et non signé. Python n’a pas de >>> parce qu’il n’a pas d’entiers de largeur fixe.
Complément à deux : comment les ordinateurs stockent les négatifs
Si un bit vaut 0 ou 1, comment encode-t-on -5 ? La réponse retenue dans les années 1960 est le complément à deux, et tout CPU moderne l’utilise.
L’approche naïve, qui consiste à réserver un bit pour le signe, a deux défauts. D’abord, on se retrouve avec +0 et -0, pénible. Ensuite, les circuits d’addition et de soustraction doivent vérifier le signe, ce qui complique le matériel. Le complément à deux règle les deux.
La règle tient en trois lignes :
- Prendre la représentation binaire positive.
- Inverser chaque bit (c’est le « complément à un »).
- Ajouter 1.
Exemple détaillé : encoder -5 en complément à deux sur 8 bits.
5 en binaire : 0000 0101
inverser les bits : 1111 1010 (c'est -6 en complément à deux !)
ajouter 1 : 1111 1011 ← voilà -5
Vérifiez avec notre convertisseur : entrez 251 (décimal) dans le Convertisseur de base, la sortie binaire donne 11111011. En contexte signé 8 bits, c’est -5 ; en non signé, c’est 251. Mêmes bits, interprétation différente.
Cela explique la surprise ~5 = -6. Le NOT bit à bit produit le complément à un. Le complément à deux est le complément à un plus 1. Donc :
~x = -(x + 1) // vrai dans tout langage en complément à deux
~5 = -6
~(-3) = 2
Pour un entier signé sur n bits, la plage va de -2ⁿ⁻¹ à 2ⁿ⁻¹ − 1. 8 bits signés couvrent -128 à 127. 32 bits signés couvrent environ -2,1 milliards à +2,1 milliards.
Neuf motifs courants de manipulation de bits
Ces neuf motifs couvrent à peu près 95 % de la manipulation de bits que vous écrirez. Mémorisez-les, vous les reconnaîtrez partout dans le code système.
Activer un bit : x | (1 << n)
Allumer le bit n, laisser les autres intacts.
let flags = 0b0100;
flags = flags | (1 << 0); // 0b0101
Effacer un bit : x & ~(1 << n)
Éteindre le bit n, laisser les autres. ~(1 << n) est un masque avec tous les bits activés sauf n.
let flags = 0b0111;
flags = flags & ~(1 << 1); // 0b0101
Basculer un bit : x ^ (1 << n)
Retourner le bit n, quel que soit son état.
let flags = 0b0100;
flags = flags ^ (1 << 2); // 0b0000
flags = flags ^ (1 << 2); // 0b0100 de nouveau
Tester un bit : (x >> n) & 1
Renvoie 1 si le bit n est à 1, 0 sinon. Forme équivalente : (x & (1 << n)) !== 0.
const flags = 0b0101;
const isBit2Set = (flags >> 2) & 1; // 1
Isoler le bit le plus bas : x & -x
Ne garde que le 1 le plus à droite de x. L’astuce marche parce qu’en complément à deux, -x vaut ~x + 1, ce qui retourne tous les bits jusqu’au plus bas inclus.
const x = 0b10110100;
const lowest = x & -x; // 0b00000100 = 4
C’est la base des arbres de Fenwick (BIT) pour des sommes préfixes en O(log n).
Compter les bits à 1 (popcount)
Compter combien de 1 dans un entier. La plupart des langages ont une fonction native :
// JavaScript (version manuelle)
const popcount = (n) => {
let count = 0;
while (n) { count += n & 1; n >>>= 1; }
return count;
};
popcount(0b10110100); // 4
# Python 3.10+
(0b10110100).bit_count() # 4
// Go
import "math/bits"
bits.OnesCount(0b10110100) // 4
Échange XOR sans variable temporaire
Tour de passe-passe classique : échanger deux entiers sans variable tierce. À ne jamais utiliser en prod (plus lent qu’une temporaire et cassé si a et b pointent la même mémoire), mais ça vaut le coup d’être compris.
let a = 5, b = 9;
a = a ^ b; // a = 5 ^ 9
b = a ^ b; // b = (5 ^ 9) ^ 9 = 5
a = a ^ b; // a = (5 ^ 9) ^ 5 = 9
// a = 9, b = 5
Détecter une puissance de 2 : (x & (x - 1)) === 0
Une puissance de 2 a exactement un bit activé. Soustraire 1 éteint ce bit et active tous ceux d’en dessous. Le AND donne zéro uniquement pour les puissances de 2 (et 0, d’où le garde x > 0).
const isPow2 = (x) => x > 0 && (x & (x - 1)) === 0;
isPow2(16); // true
isPow2(17); // false
Test d’imparité rapide : x & 1
Plus rapide que x % 2 dans certains langages, équivalent dans d’autres après optimisation. Utile dans une boucle chaude ou quand la lisibilité passe au second plan.
const isOdd = (x) => (x & 1) === 1;
Masques de bits dans du vrai code
C’est ici que les opérations bit à bit passent du tour de magie à la pratique quotidienne.
32 booléens dans un feature flag
Au lieu d’une struct avec 32 champs booléens, empaquetez le tout :
const FLAGS = {
DARK_MODE: 1 << 0,
NEW_NAV: 1 << 1,
AI_SUGGESTIONS: 1 << 2,
BETA_EDITOR: 1 << 3,
// ... jusqu'à 1 << 31
};
let userFlags = 0;
userFlags |= FLAGS.DARK_MODE | FLAGS.AI_SUGGESTIONS; // activer
if (userFlags & FLAGS.AI_SUGGESTIONS) {
showSuggestions();
}
userFlags &= ~FLAGS.DARK_MODE; // désactiver
32 booléens stockés dans 4 octets, tout sous-ensemble testable en un AND. Les bases de données adorent : une colonne au lieu de 32.
Permissions de fichiers Unix
chmod 755 est du bit à bit. Les trois chiffres octaux correspondent à trois triplets de bits :
7 = 111 (propriétaire : rwx)
5 = 101 (groupe : r-x)
5 = 101 (autres : r-x)
À essayer : dans le Convertisseur de base, source en octal, entrée 755, sortie binaire 111101101. Le système de fichiers stocke littéralement les permissions sous cette forme.
Activer uniquement « écriture de groupe » :
const perms = 0o755;
const withGroupWrite = perms | 0o020; // 0o775
Masquage de sous-réseau IP
Avec 192.168.1.10/24, extraire l’adresse réseau par AND avec le masque :
const ip = 0xC0A8010A; // 192.168.1.10
const mask = 0xFFFFFF00; // 255.255.255.0 (/24)
const network = ip & mask; // 0xC0A80100 = 192.168.1.0
Même logique pour un VLAN d’entreprise : sur un /22 fourni par un hébergeur comme OVH ou Scaleway, le masque 0xFFFFFC00 isole les dix bits de préfixe, et les six bits restants adressent jusqu’à 1 022 hôtes utiles. Les équipements réseau (Cisco, MikroTik, pfSense) effectuent exactement ce AND à chaque paquet, des millions de fois par seconde.
IDs empaquetés : Snowflake
Le Snowflake de Twitter empaquète horodatage, ID machine et séquence dans un entier 64 bits :
┌─ 1 bit ─┬─── 41 bits ───┬─ 10 bits ─┬─ 12 bits ─┐
│ signe │ horodatage │ machine │ seq │
└─────────┴───────────────┴───────────┴───────────┘
Encoder un ID tient en deux décalages et deux OR :
const id = (BigInt(timestamp) << 22n) |
(BigInt(machineId) << 12n) |
BigInt(sequence);
Décoder fait l’inverse : décalage à droite et masque. Pour choisir entre Snowflake, ULID et UUIDv7, voyez notre comparatif d’IDs distribués.
Pièges propres à chaque langage
JavaScript : piège de la coercition 32 bits
JavaScript convertit les opérandes en entiers 32 bits signés avant chaque opération bit à bit, puis reconvertit en Number. Toute valeur au-delà de 2³¹ − 1 = 2147483647 déborde.
2147483647 | 0 // 2147483647 (ok)
2147483648 | 0 // -2147483648 (débordement !)
4294967295 | 0 // -1 (tous bits à 1, interprété signé)
Pour du 64 bits, utilisez BigInt, qui dispose de ses propres opérateurs sans limite de largeur :
(2n ** 40n) | 1n // 1099511627777n
Bugs de priorité d’opérateurs
Un des bugs bit à bit les plus fréquents :
// Bug : lu comme (x & (1 == 0)) car == lie plus fort que &
if (x & 1 == 0) { /* ... */ }
// Correct : parenthèses
if ((x & 1) == 0) { /* ... */ }
En C, JavaScript, Python, Go et descendants, les opérateurs de comparaison ont priorité supérieure à AND/OR/XOR bit à bit. Dans le doute, parenthésez.
Tableau comparatif des langages
| Langage | Coercition de largeur | >> négatif | Support BigInt |
|---|---|---|---|
| JavaScript | Force 32 bits signés ; >>> non signé | arithmétique | BigInt a ses propres opérateurs |
| Python | Précision arbitraire, pas de largeur fixe | arithmétique | Natif |
| Go | Strict ; décalage doit être non signé | arithmétique pour signés | math/big |
| C/C++ | Suit le type ; int, unsigned, etc. | défini par l’implémentation pour signés | Rien en natif |
| Rust | Strict ; panic au dépassement en debug | arithmétique pour signés | u128 / crates externes |
La bizarrerie infinie de Python
Les entiers Python n’ont pas de largeur fixe, donc la logique du complément à deux s’étend « à l’infini » vers la gauche. D’où ~5 = -6 (ni 250 ni 65530) : Python traite le résultat comme un entier négatif, pas comme un motif de bits à largeur fixe. Pour une sémantique de bouclage, masquez explicitement :
# Simuler NOT 8 bits
(~5) & 0xFF # 250
La performance en 2026, pour de vrai
La croyance répandue dit que le bit à bit est « toujours plus rapide ». En 2026, c’est à moitié vrai.
Les compilateurs font déjà les réécritures évidentes. Les optimiseurs modernes transforment x * 2 en x << 1 automatiquement. Écrire x << 1 dans du code applicatif « pour la vitesse » relève du culte du cargo : ça n’aide pas, et ça dégrade la lisibilité.
Là où le bit à bit gagne vraiment :
- Boucles numériques chaudes : popcount, comptage de zéros initiaux ou finaux, moteurs d’échecs à bitboards.
- Structures compactes : filtres de Bloom, roaring bitmaps, arbres de Fenwick.
- Registres matériels et E/S mappées en mémoire : embarqué, noyau, firmware.
- Primitives cryptographiques : AES, ChaCha20, SHA, tous bâtis sur XOR, rotations et décalages.
- Compression et décompression : codage de Huffman, RLE, entiers empaquetés.
- Moteurs de bases de données : index bitmap, formats colonnes empaquetés comme Parquet.
Là où ça n’aide pas : remplacer x % 2 par x & 1 dans une fonction métier exécutée deux fois par requête. Accélération imperceptible, coût de lisibilité bien réel.
Le cas où la manipulation de bits gagne toujours, c’est l’empreinte mémoire. 32 flags dans un int, c’est 31 octets de moins que 32 booléens. À grande échelle, sur des millions d’enregistrements ou des milliards d’événements, c’est la différence entre un layout cache-friendly et un cache-miss storm.
Aide-mémoire rapide
| Opération | Opérateur | Exemple | Résultat | Usage typique |
|---|---|---|---|---|
| AND | & | 0b1100 & 0b1010 | 0b1000 | Masque/extraction |
| OR | | | 0b1100 | 0b1010 | 0b1110 | Combiner flags |
| XOR | ^ | 0b1100 ^ 0b1010 | 0b0110 | Bascule/diff |
| NOT | ~ | ~0b1100 | ...11110011 | Masque inverse |
| Décalage à gauche | << | 1 << 3 | 8 | Multiplier par 2ⁿ |
| Décalage à droite | >> | 16 >> 2 | 4 | Diviser par 2ⁿ (signé) |
| Décalage à droite non signé (JS) | >>> | -1 >>> 0 | 4294967295 | Traiter en non signé |
Activer bit n | | | x | (1 << n) | Allumer | |
Effacer bit n | & ~ | x & ~(1 << n) | Éteindre | |
Basculer bit n | ^ | x ^ (1 << n) | Retourner | |
Tester bit n | & | (x >> n) & 1 | 0 ou 1 | Interroger |
| Bit bas isolé | & - | x & -x | Isoler | |
| Puissance de 2 | & | x > 0 && (x & (x-1)) == 0 | booléen | Détecter |
FAQ
Quelle différence entre AND logique (&&) et bit à bit (&) ?
Le AND logique opère sur des booléens entiers et court-circuite : false && expr n’évalue jamais expr. Le AND bit à bit opère sur les bits individuels d’entiers et évalue toujours les deux côtés. Utilisez && pour les conditions, & pour manipuler les bits.
Pourquoi ~1 vaut-il -2 dans la plupart des langages ?
Le NOT bit à bit inverse chaque bit, produisant le complément à un. En représentation complément à deux, inverser tous les bits de x donne -(x + 1). D’où ~1 = -2, ~0 = -1, ~(-1) = 0.
x << 1 est-il vraiment plus rapide que x * 2 ?
Non, x << 1 n’est pas plus rapide que x * 2 en pratique. Tout compilateur moderne reconnaît x * 2 et émet la même instruction de décalage. Préférez x * 2 pour la clarté, et réservez << aux cas où vous pensez vraiment en bits, par exemple pour construire un masque.
JavaScript supporte-t-il les opérations bit à bit 64 bits ?
JavaScript ne supporte pas nativement le 64 bits avec les opérateurs standard &, |, ^, <<, >>, qui forcent les opérandes en 32 bits signés. Utilisez les littéraux BigInt (ex. 1n << 40n) pour des opérations bit à bit à précision arbitraire. Attention : on ne peut pas mélanger BigInt et Number dans la même expression sans conversion explicite.
Comment compter les bits à 1 efficacement ?
Pour compter les bits à 1 efficacement, utilisez la fonction native de votre langage : bits.OnesCount en Go, Integer.bitCount en Java, .bit_count() en Python 3.10+, intrinsèques popcount en C/C++. Toutes se traduisent par une seule instruction POPCNT sur x86 et ARM modernes. Évitez les boucles manuelles en code chaud : la version native est dix à vingt fois plus rapide.
Quand préférer un masque de bits à une struct de booléens ?
Masque quand vous devez stocker beaucoup de flags de façon compacte (bases de données, protocoles réseau, formats de fichier) ou tester rapidement une combinaison (flags & REQUIRED_MASK). Struct quand les champs ont des types différents ou quand la lisibilité prime sur la mémoire.
Que se passe-t-il si je décale au-delà de la largeur ?
Décaler au-delà de la largeur produit un comportement dépendant du langage : indéfini en C/C++, pris modulo 32 en JavaScript (donc 1 << 32 vaut 1, pas 0), et sans limite en Python car les entiers n’ont pas de largeur fixe (1 << 100 est juste un entier plus grand). Ne vous reposez jamais sur ce comportement ; masquez vous-même le décalage si nécessaire.
Pourquoi ~5 vaut -6 en Python et pas 2 ?
Les entiers Python n’ont pas de largeur fixe, donc le complément à deux s’étend conceptuellement à l’infini. ~5 vaut -(5 + 1) = -6, comme dans tout langage en complément à deux. Pour la valeur « inversée » 8 bits 250, masquez : (~5) & 0xFF.
Le chiffrement XOR est-il sûr ?
Un masque jetable avec une clé vraiment aléatoire aussi longue que le message est théoriquement incassable. Réutiliser la même clé sur plusieurs messages est catastrophique : le « chiffrement XOR » à clé courte répétée se casse trivialement. Les vrais algorithmes comme AES et ChaCha20 utilisent XOR en interne, mais comme une étape parmi beaucoup d’autres.
Comment écrire à la main un négatif en complément à deux ?
Écrivez la valeur positive en binaire à la largeur cible, inversez chaque bit, ajoutez 1. Exemple : -5 sur 8 bits = 00000101 → inverser 11111010 → ajouter 1 → 11111011. Vérifiez avec notre Convertisseur de base en convertissant 251 (interprétation non signée de 11111011) et en confirmant que la sortie binaire est 11111011.
Outils associés et lectures complémentaires
- Convertisseur de base : saisissez un nombre et observez les bits
- Guide de conversion de bases numériques : lecture préalable sur binaire, octal et hexa
- UUID v4 vs v7 vs ULID vs Snowflake : empaquetage de bits dans les IDs distribués
- Bonnes pratiques de sécurité : bitmaps de permissions et leurs pièges