J'ai vu ce modèle très utilisé en C et C++.
unsigned int flags = -1; // all bits are true
Est-ce un bon moyen portable d'y parvenir ? Ou est-ce que l'utilisation de 0xffffffff
o ~0
mieux ?
J'ai vu ce modèle très utilisé en C et C++.
unsigned int flags = -1; // all bits are true
Est-ce un bon moyen portable d'y parvenir ? Ou est-ce que l'utilisation de 0xffffffff
o ~0
mieux ?
Je vous recommande de le faire exactement comme vous l'avez montré, car c'est la méthode la plus simple. Initialiser à -1
qui fonctionnera toujours indépendant de la représentation effective du signe, tandis que ~
aura parfois un comportement surprenant car vous devrez avoir le bon type d'opérande. Ce n'est qu'alors que vous obtiendrez la valeur la plus élevée d'un unsigned
type.
Pour un exemple de surprise possible, considérez celle-ci :
unsigned long a = ~0u;
Il ne stockera pas nécessairement un motif dont tous les bits sont à 1 en a
. Mais il va d'abord créer un modèle avec tous les bits 1 dans un unsigned int
puis l'affecter à a
. Que se passe-t-il lorsque unsigned long
a plus de bits est que ceux-ci ne sont pas tous égaux à 1.
Et considérez celle-ci, qui échouera sur une représentation non complémentaire à deux :
unsigned int a = ~0; // Should have done ~0u !
La raison en est que ~0
doit inverser tous les bits. En inversant cela, on obtient -1
sur une machine à complément à deux (ce qui est la valeur dont nous avons besoin !), mais elle sera no rendement -1
sur une autre représentation. Sur une machine à complément à un, cela donne zéro. Ainsi, sur une machine à complément à un, ce qui précède initialisera a
à zéro.
Ce que vous devez comprendre, c'est qu'il s'agit de valeurs, et non de bits. La variable est initialisée avec un valeur . Si dans l'initialisateur vous modifiez les bits de la variable utilisée pour l'initialisation, la valeur sera générée en fonction de ces bits. La valeur dont vous avez besoin, pour initialiser a
à la valeur la plus élevée possible, est -1
o UINT_MAX
. La seconde dépendra du type de a
- vous devrez utiliser ULONG_MAX
para un unsigned long
. Cependant, le premier ne dépendra pas de son type, et c'est un bon moyen d'obtenir la valeur la plus élevée.
Nous sommes no de savoir si -1
a tous les bits un (ce n'est pas toujours le cas). Et nous no de savoir si ~0
a tous les bits un (c'est le cas, bien sûr).
Mais ce dont nous parlons est le résultat de l'initialisation. flags
variable est. Et pour ça, uniquement -1
fonctionnera avec tous les types et toutes les machines.
Pourquoi la conversion de -1 en tous les 1 est-elle garantie ? Est-ce garanti par la norme ?
La conversion qui se produit est qu'il ajoute de manière répétée un plus que ULONG_MAX jusqu'à ce qu'il soit dans la plage (6.3.1.3 dans le projet C TC2). En C++, c'est la même chose, mais on utilise une autre façon de le formaliser (modulo 2^n). Tout se résume à des relations mathématiques.
Au lieu de -1
vous pouvez également utiliser UINTMAX_MAX
de stdint.h comme valeur d'initialisation agnostique ; mais on peut supposer que le programmeur connaît le nombre de bits significatifs de la variable flags, il n'y a donc rien de mal à ce que la variable 0xff...
soit
unsigned int flags = -1;
est portable.unsigned int flags = ~0;
n'est pas portable car il s'appuie sur une représentation de complément à deux.unsigned int flags = 0xffffffff;
n'est pas portable car il suppose des ints 32 bits.Si vous voulez définir tous les bits d'une manière garantie par la norme C, utilisez la première.
Comment ~0 (c'est-à-dire l'opérateur de complément à un) s'appuie-t-il sur la représentation de complément à deux ?
Vous êtes à l'envers. Le fait de mettre les drapeaux à -1 repose sur une représentation en complément à deux. Dans une représentation signe+magnitude, moins un n'a que deux bits définis : le bit du signe et le bit le moins significatif de la magnitude.
La norme C exige que la valeur int de zéro ait son bit de signe et que tous les bits de valeur soient à zéro. Après le complément à un, tous ces bits sont à un. Les valeurs d'un int avec tous les bits définis sont : Sign-et-magnitude : INT_MIN Complément à un : -0 Complément à deux : -1 Ainsi, l'instruction "unsigned int flags = ~0 ;" attribuera la valeur ci-dessus correspondant à la représentation entière de la plate-forme. Mais le '-1' du complément à deux est le seul qui mettra tous les bits de flags à un.
Franchement, je pense que tous les fff sont plus lisibles. Quant au commentaire selon lequel il s'agit d'un anti-modèle, si vous vous souciez vraiment de savoir si tous les bits sont activés ou désactivés, je dirais que vous êtes probablement dans une situation où vous vous souciez de la taille de la variable de toute façon, ce qui nécessiterait quelque chose comme boost::uint16_t, etc.
Il existe un certain nombre de cas dans lesquels vous ne vous souciez pas tant que ça, mais ils sont rares. Par exemple, les algorithmes qui travaillent sur des ensembles de données de N bits en les décomposant en morceaux de taille sizeof(unsigned)*CHAR_BIT bits chacun.
@Zoidberg'-- Cela ne fonctionne pas sur les systèmes autres que le complément à deux. Par exemple, sur un système signe-magnitude, ~0
est un entier dont tous les bits sont à 1, mais lorsque vous attribuez ensuite ce int
à la unsigned
variable flags
vous effectuez une conversion de valeur de -2**31
(en supposant que l'on dispose de 32 bits int
) à (-2**31 % 2**32) == 2**31
qui est un nombre entier dont tous les bits sauf le premier sont à 1.
Je l'aime bien parce qu'elle est standard, mais elle est trop verbeuse et vous oblige à indiquer le type deux fois. Utiliser ~0 est probablement plus sûr puisque 0 peut être n'importe quel type d'entier. (Bien que je sois conscient que cela sent trop le C).
Le fait qu'il soit verbeux peut être considéré comme un avantage. Mais j'aime bien aussi ~0.
Vous pouvez atténuer le manque de clarté avec la macro standard UINT_MAX, puisque vous codifiez en dur le type unsigned int de toute façon.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
1 votes
Je ne comprends pas, pouvez-vous m'expliquer ?
8 votes
Je pense que la question la plus importante est de savoir si la signification du code est claire. Même si
-1
fonctionnera toujours, le fait qu'un commentaire soit nécessaire après cela montre que ce n'est pas un code clair. Si la variable est censée être une collection de drapeaux, pourquoi lui attribuer un nombre entier ? Son type peut être un entier, mais il n'est certainement pas sémantiquement un entier. Vous n'allez jamais l'incrémenter ou la multiplier. J'utiliserais donc0xffffffff
pas pour la portabilité ou la correction, mais pour la clarté.0 votes
@CamJackson le commentaire n'est pas et toute personne écrivant du code C pourrait être familière avec la façon dont les valeurs sont représentées.
0 votes
La question était à l'origine correctement étiquetée comme C et C++. Les langages peuvent diverger dans la mesure où le C++ a proposé d'exiger le complément à deux. Ceci étant dit, cela ne change rien au fait que
-1
reste une solution portable et rétrocompatible pour les deux langues, mais elle pourrait affecter certains raisonnements dans d'autres réponses.