148 votes

Est-il sûr d'utiliser -1 pour mettre tous les bits à vrai ?

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 ?

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 donc 0xffffffff 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.

170voto

Johannes Schaub - litb Points 256113

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.

10 votes

Pourquoi la conversion de -1 en tous les 1 est-elle garantie ? Est-ce garanti par la norme ?

9 votes

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.

1 votes

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

59voto

Dingo Points 2238
  • 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.

11 votes

Comment ~0 (c'est-à-dire l'opérateur de complément à un) s'appuie-t-il sur la représentation de complément à deux ?

12 votes

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.

15 votes

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.

24voto

Doug T. Points 33360

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.

0 votes

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.

2 votes

+1. Même si la taille du type de données est plus grande que le nombre de F (c'est-à-dire que vous n'avez pas tout à fait mis tous les bits à vrai), puisque vous définissez explicitement la valeur, vous êtes au moins conscient des bits qui sont "sûrs à utiliser" .

17voto

hammar Points 89293

Une façon d'éviter les problèmes mentionnés est de simplement faire :

unsigned int flags = 0;
flags = ~flags;

Portable et précis.

4 votes

Mais alors vous perdez la possibilité de déclarer flags como const .

1 votes

@DavidStone unsigned int const flags = ~0u;

0 votes

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

14voto

Edouard A. Points 5047

Je ne suis pas sûr que l'utilisation d'un int non signé pour les drapeaux soit une bonne idée en premier lieu en C++. Qu'en est-il des bitset et autres ?

std::numeric_limit<unsigned int>::max() est meilleur parce que 0xffffffff suppose que unsigned int est un entier de 32 bits.

0 votes

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

0 votes

Le fait qu'il soit verbeux peut être considéré comme un avantage. Mais j'aime bien aussi ~0.

2 votes

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

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.

Powered by:

X