Est-il sûr d'utiliser -1 pour définir tous les bits sur true?

132

J'ai vu ce modèle beaucoup utilisé en C & C ++.

unsigned int flags = -1;  // all bits are true

Est-ce un bon moyen portable d'accomplir cela? Ou utilise-t-il 0xffffffffou ~0mieux?

hyperlogique
la source
1
Je ne comprends pas, pouvez-vous expliquer?
corazza
8
Je pense que la question la plus importante est de savoir si le sens du code est clair. Même si -1cela fonctionnera toujours, le fait qu'un commentaire soit nécessaire après montre que ce n'est pas du code clair. Si la variable est censée être une collection d'indicateurs, pourquoi lui attribuer un entier? Son type peut être un entier, mais ce n'est certainement pas un entier sémantiquement. Vous n'allez jamais l'incrémenter ou le multiplier. Je n'utiliserais donc 0xffffffffpas pour la portabilité ou l'exactitude, mais pour la clarté.
Cam Jackson
@CamJackson le commentaire n'est pas et quiconque écrit du code C pourrait être familier avec la façon dont les valeurs sont représentées.
Miles Rout
La question était à l'origine correctement étiquetée comme C et C ++. Les langages peuvent être divergents dans la mesure où C ++ a une proposition d'exiger un complément à deux. Cela dit, cela ne change pas le fait qu'il -1reste une solution portable et rétrocompatible pour les deux langues, mais cela pourrait affecter une partie du raisonnement dans d'autres réponses.
Adrian McCarthy

Réponses:

154

Je vous recommande de le faire exactement comme vous l'avez montré, car c'est le plus simple. Initialiser à -1qui fonctionnera toujours , indépendamment de la représentation réelle du signe, alors que cela ~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 unsignedtype.

Pour un exemple de surprise possible, considérez celle-ci:

unsigned long a = ~0u;

Il ne stockera pas nécessairement un motif avec tous les bits 1 dans a. Mais il créera d'abord un motif avec tous les bits 1 dans un unsigned int, puis l'attribuera a. Ce qui se passe quand il y unsigned longa plus de bits, c'est que tous ne valent pas 1.

Et considérez celui-ci, qui échouera sur une représentation de complément non-deux:

unsigned int a = ~0; // Should have done ~0u !

La raison en est qu'il ~0faut inverser tous les bits. Qui donnera à inverser -1sur une machine de complément à deux ( ce qui est la valeur dont nous avons besoin!), Mais pas céder -1à 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 s'initialise aà zéro.

Ce que vous devez comprendre, c'est qu'il s'agit de valeurs - pas de bits. La variable est initialisée avec une valeur . Si dans l'initialiseur 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 -1ou UINT_MAX. Le second dépendra du type de a- vous devrez utiliser ULONG_MAXpour un fichier 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 ne parlons pas de savoir si -1tous les bits sont un (ce n'est pas toujours le cas). Et nous ne parlons pas de savoir si ~0tous les bits sont un (il en a, bien sûr).

Mais ce dont nous parlons est le résultat de la flagsvariable initialisée . Et pour cela, seul-1 fonctionnera avec tous les types et machines.

Johannes Schaub - litb
la source
9
pourquoi -1 est-il garanti d'être converti en tous? Est-ce que c'est garanti par la norme?
jalf
9
la conversion qui se produit est qu'il ajoute à plusieurs reprises un de 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 pareil, en utilisant simplement une autre façon de le formaliser (modulo 2 ^ n). Tout se résume aux relations mathématiques.
Johannes Schaub - litb
6
@litb: la conversion de -1 est certainement un bon moyen d'obtenir des valeurs maximales non signées, mais ce n'est pas vraiment descriptif; c'est la raison pour laquelle les constantes _MAX existent (SIZE_MAX a été ajouté en C99); d'accord, la version C ++ numeric_limits<size_t>::max()est un peu longue, mais le casting aussi ...
Christoph
11
"Nous ne parlons pas de savoir si -1 a tous les bits un (ce n'est pas toujours le cas). Et nous ne parlons pas de savoir si ~ 0 a tous les bits un (il a, bien sûr)." - quoi ??? Je pensais que le but était de mettre tous les bits à 1. C'est comme ça que les drapeaux fonctionnent ... non ?? Vous regardez les bits . Qui se soucie de la valeur?
mpen
14
@Mark le questionneur s'en soucie. Il demande "Est-il sûr d'utiliser -1 pour mettre tous les bits à true". Cela ne demande pas quels bits -1sont représentés par, ni ce que les bits ~0ont. Nous ne nous soucions peut-être pas des valeurs, mais le compilateur le fait. Nous ne pouvons pas ignorer le fait que les opérations fonctionnent avec et par des valeurs. La valeur de ~0peut ne pas être -1, mais c'est la valeur dont vous avez besoin. Voir ma réponse et le résumé de @ Dingo.
Johannes Schaub - litb
49
  • unsigned int flags = -1; est portable.
  • unsigned int flags = ~0; n'est pas portable car il repose sur une représentation complémentaire à deux.
  • unsigned int flags = 0xffffffff; n'est pas portable car il suppose des entiers 32 bits.

Si vous souhaitez définir tous les bits d'une manière garantie par le standard C, utilisez le premier.

Dingo
la source
10
Comment ~ 0 (c'est-à-dire l'opérateur du complément à un) repose-t-il sur la représentation du complément à deux?
Drew Hall
11
Vous avez cela à l'envers. Son paramétrage des indicateurs à -1 qui repose sur une représentation complémentaire à deux. Dans une représentation signe + magnitude moins un, seuls deux bits sont définis: le bit de signe et le bit le moins significatif de la magnitude.
Stephen C. Steel
15
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 ne font qu'un. Les valeurs d'un int avec tous les bits définis sont: Sign-and-magnitude: INT_MIN Complément à un: -0 Complément à deux: -1 Donc, l'instruction "unsigned int flags = ~ 0;" attribuera la valeur ci-dessus correspondant à la représentation entière de la plateforme. Mais le '-1' du complément à deux est le seul qui mettra tous les bits de fanion à un.
Dingo
9
@Stephen: D'accord sur la représentation. Mais quand une valeur int est assignée à un int unsigned, le unsigned n'obtient pas sa valeur en adoptant la représentation interne de la valeur int (sauf dans les systèmes à complément à deux où cela fonctionne généralement). Toutes les valeurs affectées aux entiers non signés sont modulo (UINT_MAX + 1), donc l'affectation de -1 fonctionne quelle que soit la représentation interne.
Dingo
20
@Mark: vous confondez deux opérations. ~0donne une intvaleur avec tous les bits définis, bien sûr. Mais attribuer un intà un unsigned intne conduit pas nécessairement à un int non signé ayant le même modèle de bits que le modèle de bits signé. Ce n'est toujours le cas qu'avec une représentation complémentaire à 2. Sur un complément à 1 ou une représentation de grandeur de signe, l'attribution d'une intvaleur négative à un unsigned intproduit un motif de bits différent. En effet, la norme C ++ définit la conversion signée -> non signée comme étant la valeur modulo-égale, et non la valeur avec les mêmes bits.
Steve Jessop
25

Franchement, je pense que tous les fff sont plus lisibles. En ce qui concerne le commentaire selon lequel c'est un anti-modèle, si vous vous souciez vraiment que tous les bits soient définis / effacé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 appellerait quelque chose comme boost :: uint16_t, etc.

Doug T.
la source
Il y a un certain nombre de cas dans lesquels vous ne vous souciez pas beaucoup, mais ils sont rares. Par exemple, des algorithmes qui fonctionnent sur des ensembles de données de N bits en les décomposant en morceaux de taille de (non signés) * CHAR_BIT bits chacun.
MSalters
2
+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 défini tous les bits sur true), puisque vous définissez explicitement la valeur, vous savez au moins quels bits sont "sûrs" utiliser "..
mpen
17

Une manière qui évite les problèmes mentionnés est de faire simplement:

unsigned int flags = 0;
flags = ~flags;

Portable et précis.

Hammar
la source
2
Mais alors vous perdez la possibilité de déclarer flagscomme const.
David Stone
1
@DavidStoneunsigned int const flags = ~0u;
@Zoidberg '- Cela ne fonctionne pas sur des systèmes autres que le complément à deux. Par exemple, sur un système de grandeur de signe, ~0est un entier dont tous les bits sont définis sur 1, mais lorsque vous l'assignez ensuite intà la unsignedvariable flags, vous effectuez une conversion de valeur de -2**31(en supposant un 32 bits int) à (-2**31 % 2**32) == 2**31, qui est un entier avec tous les bits sauf le premier set à 1.
David Stone
C'est aussi une raison pour laquelle cette réponse est dangereuse. Il semble que la réponse de @Zoidberg '- soit identique, mais ce n'est pas le cas. Cependant, en tant que personne lisant le code, je devrais y réfléchir pour comprendre pourquoi vous avez pris deux étapes pour l'initialiser, et peut-être être tenté de changer cela en une seule étape.
David Stone
2
Ah oui, je n'ai pas remarqué le usuffixe dans votre réponse. Cela fonctionnerait bien sûr, mais cela pose toujours le problème de spécifier le type de données que vous utilisez ( unsignedet pas plus grand) deux fois, ce qui pourrait entraîner des erreurs. L'erreur est plus susceptible d'apparaître si l'affectation et la déclaration de variable initiale sont plus éloignées, cependant.
David Stone
13

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

std::numeric_limit<unsigned int>::max()est mieux car 0xffffffffsuppose que unsigned int est un entier 32 bits.

Édouard A.
la source
J'aime cela à cause de sa norme, mais c'est trop verbeux et cela vous oblige à énoncer le type deux fois. L'utilisation de ~ 0 est probablement plus sûre car 0 peut être n'importe quel type entier. (Bien que je sache que ça sent trop le C.)
Macke
Le fait qu'il soit verbeux peut être considéré comme un avantage. Mais j'aime aussi ~ 0.
Edouard A.
2
Vous pouvez atténuer la verbosité avec la macro standard UINT_MAX, puisque vous codez en dur le type unsigned int de toute façon.
2
@Macke Vous pouvez éviter d'indiquer le type en C ++ 11 avec auto. auto const flags = std::numeric_limit<unsigned>::max().
David Stone
11
unsigned int flags = -1;  // all bits are true

"Est-ce un bon moyen [,] portable d'accomplir cela?"

Portable? Oui .

Bien? Discutable , comme en témoigne toute la confusion montrée sur ce fil. Être suffisamment clair pour que vos collègues programmeurs puissent comprendre le code sans confusion devrait être l'une des dimensions que nous mesurons pour un bon code.

En outre, cette méthode est sujette aux avertissements du compilateur . Pour éluder l'avertissement sans paralyser votre compilateur, vous auriez besoin d'un cast explicite. Par exemple,

unsigned int flags = static_cast<unsigned int>(-1);

La distribution explicite nécessite que vous prêtiez attention au type de cible. Si vous faites attention au type de cible, vous éviterez naturellement les pièges des autres approches.

Mon conseil serait de faire attention au type de cible et de s'assurer qu'il n'y a pas de conversions implicites. Par exemple:

unsigned int flags1 = UINT_MAX;
unsigned int flags2 = ~static_cast<unsigned int>(0);
unsigned long flags3 = ULONG_MAX;
unsigned long flags4 = ~static_cast<unsigned long>(0);

Tout cela est correct et plus évident pour vos collègues programmeurs.

Et avec C ++ 11 : nous pouvons utiliser autopour rendre l'un de ceux-ci encore plus simple:

auto flags1 = UINT_MAX;
auto flags2 = ~static_cast<unsigned int>(0);
auto flags3 = ULONG_MAX;
auto flags4 = ~static_cast<unsigned long>(0);

Je considère comme correct et évident mieux que simplement correct.

Adrian McCarthy
la source
10

La conversion de -1 en n'importe quel type non signé est garantie par la norme pour aboutir à tout-un. L'utilisation de ~0Uest généralement mauvaise car 0a type unsigned intet ne remplira pas tous les bits d'un type non signé plus grand, à moins que vous n'écriviez explicitement quelque chose comme ~0ULL. Sur des systèmes sains, ~0devrait être identique à-1 , mais comme la norme autorise les représentations complémentaires à un et signe / grandeur, à proprement parler, elle n'est pas portable.

Bien sûr, il est toujours acceptable d'écrire 0xffffffffsi vous savez que vous avez besoin d'exactement 32 bits, mais -1 a l'avantage qu'il fonctionnera dans n'importe quel contexte même lorsque vous ne connaissez pas la taille du type, comme les macros qui fonctionnent sur plusieurs types , ou si la taille du type varie selon l'implémentation. Si vous ne connaissez le type, un autre moyen sûr d'obtenir tous-ones est les macros limites UINT_MAX, ULONG_MAX, ULLONG_MAX, etc.

Personnellement, j'utilise toujours -1. Cela fonctionne toujours et vous n'avez pas à y penser.

R .. GitHub STOP AIDING ICE
la source
FWIW, si je veux dire «tous les 1 bits», j'utilise ~(type)0(bien, remplissez bien typesûr la droite ). La conversion de zéro entraîne toujours un zéro, donc c'est clair, et la négation de tous les bits du type cible est assez clairement définie. Ce n'est pas si souvent que je veux vraiment cette opération; YMMV.
Donal Fellows
6
@Donal: vous avez tout simplement tort. C spécifie que, lors de la conversion d'une valeur qui ne rentre pas dans un type non signé, les valeurs sont réduites modulo 2 ^ n où n est le nombre de bits dans le type de destination. Cela s'applique à la fois aux valeurs signées et aux types non signés plus grands. Cela n'a rien à voir avec le complément de deux.
R .. GitHub STOP AIDER ICE
2
Ils font mettre en œuvre la même, ce qui est trivial; vous utilisez simplement un opcode de soustraction non signé au lieu d'un code signé ou d'une neginstruction. Les machines qui ont un comportement arithmétique signé faux ont des opcodes arithmétiques signés / non signés séparés. Bien sûr, un très bon compilateur ignorerait toujours les opcodes signés même pour les valeurs signées et obtiendrait ainsi gratuitement un complément à deux.
R .. GitHub STOP AIDER ICE
1
@R .: Le var = ~(0*var)cas échouera pour varêtre un type non signé plus étroit que int. Peut var = ~(0U*var)- être ? (Personnellement, je préfère toujours -1, cependant).
caf
1
Cette réponse est beaucoup plus claire que celle de Johannes Schaub. Cependant, l'attribution d'un entier littéral négatif à un type non signé sans conversion entraîne généralement un avertissement du compilateur. La suppression de l'avertissement nécessite un cast, ce qui signifie qu'il faut de toute façon faire attention au type de cible, vous pouvez donc aussi bien utiliser UINT_MAX ou ULONG_MAX et être clair plutôt que de vous fier à un petit détail dans la norme, ce qui confond clairement beaucoup de vos collègues programmeurs .
Adrian McCarthy
5

Tant que vous avez l' #include <limits.h>un de vos includes, vous devez simplement utiliser

unsigned int flags = UINT_MAX;

Si vous voulez une valeur longue de bits, vous pouvez utiliser

unsigned long flags = ULONG_MAX;

Il est garanti que tous les bits de valeur du résultat sont définis sur 1, quelle que soit la manière dont les entiers signés sont implémentés.

Michael Norrish
la source
1
les constantes que vous avez suggérées sont en fait définies dans limits.h - stdint.h contient les limites pour les types d'entiers supplémentaires (entiers de taille fixe, intptr_t, ...)
Christoph
5

Oui. Comme mentionné dans d'autres réponses, -1est le plus portable; cependant, il n'est pas très sémantique et déclenche des avertissements du compilateur.

Pour résoudre ces problèmes, essayez cette aide simple:

static const struct All1s
{
    template<typename UnsignedType>
    inline operator UnsignedType(void) const
    {
        static_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");
        return static_cast<UnsignedType>(-1);
    }
} ALL_BITS_TRUE;

Usage:

unsigned a = ALL_BITS_TRUE;
uint8_t  b = ALL_BITS_TRUE;
uint16_t c = ALL_BITS_TRUE;
uint32_t d = ALL_BITS_TRUE;
uint64_t e = ALL_BITS_TRUE;
Diamant Python
la source
3
En tant que programmeur C, un code comme celui-ci me donne de mauvais rêves la nuit.
jforberg
Et que se passe-t-il lorsque vous l'utilisez dans un contexte comme ALL_BITS_TRUE ^ aaest un entier signé? Le type reste entier signé et le motif binaire (représentation d'objet) dépend du fait que la cible soit le complément de 2 ou non.
Peter Cordes
Non, ALL_BITS_TRUE ^ adonne une erreur de compilation car ALL_BITS_TRUEc'est ambigu. Il pourrait être utilisé comme uint32_t(ALL_BITS_TRUE) ^ a, cependant. Vous pouvez l'essayer vous-même sur cpp.sh :) Aujourd'hui, j'ajouterais un static_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");dans le operatorpour être sûr que les utilisateurs n'essaient pas de l'utiliser int(ALL_BITS_TRUE). Je mettrai à jour la réponse.
Diamond Python
3

Je ne ferais pas la chose -1. C'est plutôt non intuitif (du moins pour moi). Assigner des données signées à une variable non signée semble simplement être une violation de l'ordre naturel des choses.

Dans votre situation, j'utilise toujours 0xFFFF. (Utilisez le bon nombre de F pour la taille variable bien sûr.)

[BTW, je vois très rarement l'astuce -1 en code réel.]

De plus, si vous tenez vraiment sur les bits individuels dans un vairable, ce serait une bonne idée de commencer à utiliser la largeur fixe uint8_t, uint16_t, uint32_ttypes.

myron-semack
la source
2

Sur les processeurs IA-32 d'Intel, il est possible d'écrire 0xFFFFFFFF dans un registre 64 bits et d'obtenir les résultats attendus. En effet, IA32e (l'extension 64 bits de IA32) ne prend en charge que les instantanés 32 bits. Dans les instructions 64 bits, les instantanés 32 bits sont étendus à 64 bits.

Ce qui suit est illégal:

mov rax, 0ffffffffffffffffh

Ce qui suit met 64 1 dans RAX:

mov rax, 0ffffffffh

Juste pour être complet, ce qui suit met 32 ​​1 dans la partie inférieure de RAX (aka EAX):

mov eax, 0ffffffffh

Et en fait, des programmes ont échoué lorsque je voulais écrire 0xffffffff dans une variable 64 bits et que j'ai obtenu un 0xffffffffffffffff à la place. En C, ce serait:

uint64_t x;
x = UINT64_C(0xffffffff)
printf("x is %"PRIx64"\n", x);

le résultat est:

x is 0xffffffffffffffff

J'ai pensé à poster ceci comme un commentaire à toutes les réponses qui disaient que 0xFFFFFFFF supposait 32 bits, mais tellement de gens y ont répondu que j'ai pensé que je l'ajouterais comme réponse séparée.

Nathan Fellman
la source
1
Félicitations, vous avez trouvé un bogue du compilateur!
tc.
Cela a-t-il été documenté quelque part comme un bogue?
Nathan Fellman
1
En supposant qu'il se UINT64_C(0xffffffff)développe en quelque chose comme 0xffffffffuLL, c'est certainement un bogue du compilateur. La norme C discute largement des valeurs , la valeur représentée par0xffffffff est 4294967295 (et non 36893488147419103231), et il n'y a aucune conversion en types entiers signés en vue.
tc.
2

Voir la réponse de litb pour une explication très claire des problèmes.

Mon désaccord est que, à strictement parler, il n'y a aucune garantie dans les deux cas. Je ne connais aucune architecture qui ne représente pas une valeur non signée de `` un de moins de deux à la puissance du nombre de bits '' comme tous les bits définis, mais voici ce que dit réellement la norme (3.9.1 / 7 plus note 44):

Les représentations des types intégraux doivent définir des valeurs en utilisant un système de numération binaire pur. [Note 44:] Une représentation positionnelle pour les entiers qui utilise les chiffres binaires 0 et 1, dans laquelle les valeurs représentées par des bits successifs sont additives, commencent par 1 et sont multipliées par une puissance intégrale successive de 2, sauf peut-être pour le bit avec la position la plus élevée.

Cela laisse la possibilité pour l'un des bits d'être n'importe quoi.

James Hopkin
la source
En général, nous ne pouvons pas être sûrs de la valeur des bits de remplissage. Et si nous le voulons, nous pourrions être en danger car nous pourrions générer une représentation de piège pour eux (et cela pourrait soulever des signaux). Cependant, le std exige que les caractères non signés n'aient pas de bits de remplissage, et en 4.7 / 2 dans la norme c ++, il est dit que la conversion d'un entier en un type non signé, la valeur de la variable non signée résultante est la plus petite valeur congruente à la valeur entière source, (modulo 2 ^ n, n == nombre de bits dans le type non signé). alors (-1) == ((2 ^ n) -1) (mod 2 ^ n). 2 ^ n-1 a tous les bits définis dans un système de numérotation binaire pur.
Johannes Schaub - litb
Si nous voulons vraiment avoir tous les bits 1 dans la représentation objet d'un type non signé, nous aurions besoin de memset. Mais nous pourrions générer une représentation piège de cette manière :( Quoi qu'il en soit, une implémentation n'a probablement aucune raison de jeter un peu de ses entiers non signés afin qu'elle l'utilise pour stocker ses valeurs. Mais vous avez un très bon point - rien ne s'arrête une interprétation d'avoir quelques bêtises idiotes, je pense (à part in char / signed char / unsigned char, qui ne doivent pas avoir ceux-là). +1 bien sûr :)
Johannes Schaub - litb
En fin de compte, je pense que la norme pourrait être plus claire à quelle représentation elle se réfère en 4.7 / 2. Si cela fait référence à la représentation de l'objet, alors il n'y a plus de place pour les bits de remplissage (j'ai vu des gens se disputer comme cela avec lequel je ne vois rien de mal). Mais je pense que cela parle de la représentation de la valeur (parce que tout dans 4.7 / 2 est de toute façon une question de valeurs - et alors les bits de remplissage peuvent s'emboîter à côté des bits de valeur.
Johannes Schaub - litb
1
La norme semble avoir assez clairement à l'esprit les représentations du «complément de 2, du complément de 1 et de la magnitude signée», mais ne veut rien exclure. Point intéressant sur les représentations de piégeage aussi. Pour autant que je sache, le bit que j'ai cité est la définition du `` système de numération binaire pur '' en ce qui concerne la norme - le bit `` sauf '' à la fin est vraiment mon seul doute sur la question de savoir si le casting -1 est garanti travail.
James Hopkin
2

Bien que le 0xFFFF(ou 0xFFFFFFFF, etc.) puisse être plus facile à lire, il peut interrompre la portabilité du code qui serait autrement portable. Prenons, par exemple, une routine de bibliothèque pour compter le nombre d'éléments dans une structure de données dont certains bits sont définis (les bits exacts étant spécifiés par l'appelant). Le sous-programme peut être totalement indépendant de ce que représentent les bits, mais doit tout de même avoir une constante «tous les bits définis». Dans un tel cas, -1 sera bien meilleur qu'une constante hexadécimale car il fonctionnera avec n'importe quelle taille de bit.

L'autre possibilité, si une typedefvaleur est utilisée pour le masque de bits, serait d'utiliser ~ (bitMaskType) 0; si le masque de bits n'est qu'un type 16 bits, cette expression n'aura que 16 bits (même si 'int' serait autrement 32 bits) mais comme 16 bits seront tout ce qui est nécessaire, les choses devraient bien se passer à condition que utilise en fait le type approprié dans le transtypage.

Incidemment, les expressions du formulaire longvar &= ~[hex_constant]ont un mauvais piège si la constante hexadécimale est trop grande pour tenir dans un int, mais rentre dans un unsigned int. Si an intvaut 16 bits, alors longvar &= ~0x4000;ou longvar &= ~0x10000; effacera un bit de longvar, mais longvar &= ~0x8000;effacera le bit 15 et tous les bits au-dessus. Les valeurs qui s'intègrent intauront l'opérateur de complément appliqué à un type int, mais le résultat sera étendu au signe long, définissant les bits supérieurs. Les valeurs qui sont trop grandes pour unsigned intauront l'opérateur de complément appliqué au type long. Les valeurs comprises entre ces tailles appliqueront cependant l'opérateur de complément au type unsigned int, qui sera ensuite converti en type longsans extension de signe.

supercat
la source
1

Pratiquement: oui

Théoriquement: non.

-1 = 0xFFFFFFFF (ou quelle que soit la taille d'un int sur votre plate-forme) n'est vrai qu'avec l'arithmétique du complément à deux. En pratique, cela fonctionnera, mais il existe des machines héritées (mainframes IBM, etc.) où vous avez un bit de signe réel plutôt qu'une représentation de complément à deux. La solution ~ 0 que vous proposez devrait fonctionner partout.

Drew Hall
la source
6
Je l'ai dit aussi. Mais ensuite, j'ai réalisé que j'avais tort, car -1 signé se convertit toujours en max_value unsigned sous les règles de conversion, quelles que soient les représentations de valeur. Au moins, c'est le cas en C ++, je n'ai pas le standard C sous la main.
Steve Jessop
6
il y a une ambiguïté. -1 n'est pas 0xFFFFFFFF. Mais -1 est 0xFFFFFFFF s'il est converti en un entier non signé (ayant 32 bits). C'est ce qui rend cette discussion si difficile je pense. Beaucoup de gens ont des choses très différentes à l'esprit quand ils parlent de ces chaînes de bits.
Johannes Schaub - litb
1

Comme d'autres l'ont mentionné, -1 est la manière correcte de créer un entier qui sera converti en un type non signé avec tous les bits mis à 1. Cependant, la chose la plus importante en C ++ est d'utiliser des types corrects. Par conséquent, la bonne réponse à votre problème (qui comprend la réponse à la question que vous avez posée) est la suivante:

std::bitset<32> const flags(-1);

Celui-ci contiendra toujours la quantité exacte de bits dont vous avez besoin. Il construit un std::bitsetavec tous les bits mis à 1 pour les mêmes raisons mentionnées dans d'autres réponses.

David Stone
la source
0

C'est certainement sûr, car -1 aura toujours tous les bits disponibles, mais j'aime mieux ~ 0. -1 n'a tout simplement pas beaucoup de sens pour un unsigned int. 0xFF... n'est pas bon car cela dépend de la largeur du type.

Zifre
la source
4
"0xFF ... n'est pas bon car cela dépend de la largeur du type" Ce qui est la seule voie sensée à suivre, je pense. Vous êtes censé définir clairement ce que signifie chaque drapeau / bit dans votre programme. Donc, si vous définissez que vous utilisez les 32 bits les plus bas pour stocker les indicateurs, vous devez vous limiter à utiliser ces 32 bits, que la taille réelle de l'int soit 32 ou 64.
Juan Pablo Califano
0

Je dis:

int x;
memset(&x, 0xFF, sizeof(int));

Cela vous donnera toujours le résultat souhaité.

Alex
la source
4
Pas sur les systèmes avec des caractères 9 bits!
tc.
0

Tirer parti du fait qu'affecter tous les bits à un pour un type non signé équivaut à prendre la valeur maximale possible pour le type donné
et à étendre la portée de la question à tous les non signés les types d'entiers :

L'affectation de -1 fonctionne pour tout type entier non signé signé (unsigned int, uint8_t, uint16_t, etc.) pour C et C ++.

Comme alternative, pour C ++, vous pouvez soit:

  1. Comprendre <limits> et utiliserstd::numeric_limits< your_type >::max()
  2. Ecrire une fonction basée sur un modèle personnalisé (cela permettrait également une vérification de la cohérence, c'est-à-dire si le type de destination est vraiment un type non signé)

L'objectif pourrait être d'ajouter plus de clarté, car l'attribution -1nécessiterait toujours des commentaires explicatifs.

Antonio
la source
0

Un moyen de rendre le sens un peu plus évident et pourtant d'éviter de répéter le type:

const auto flags = static_cast<unsigned int>(-1);
FlorianH
la source
-6

oui la représentation montrée est tout à fait correcte car si on le fait dans l'autre sens u nécessitera un opérateur pour inverser tous les bits mais dans ce cas la logique est assez simple si l'on considère la taille des entiers dans la machine

par exemple, dans la plupart des machines, un entier vaut 2 octets = 16 bits la valeur maximale qu'il peut contenir est 2 ^ 16-1 = 65535 2 ^ 16 = 65536

0% 65536 = 0-1% 65536 = 65535 qui correspond à 1111 ............. 1 et tous les bits sont mis à 1 (si l'on considère les classes de résidus mod 65536) donc c'est beaucoup simple.

j'imagine

non si vous considérez cette notion, il est parfaitement dîné pour les entiers non signés et cela fonctionne réellement

vérifiez simplement le fragment de programme suivant

int main() {

unsigned int a=2;

cout<<(unsigned int)pow(double(a),double(sizeof(a)*8));

unsigned int b=-1;

cout<<"\n"<<b;

getchar();

return 0;

}

réponse pour b = 4294967295 whcih est -1% 2 ^ 32 sur des entiers de 4 octets

il est donc parfaitement valable pour les entiers non signés

en cas de divergence, rapport plzz

Ankit sablok
la source
9
Deux commentaires: premièrement, vous vous trompez complètement sur la taille des entiers sur «la plupart» des machines. Deuxièmement, ur txt iz très difficile 2 lu en raison de 2 ur nous de type somme de langage seckrit. Plz-nous un anglais simple .
Konrad Rudolph