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 0xffffffff
ou ~0
mieux?
c++
c
binary
bit-fields
hyperlogique
la source
la source
-1
cela 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 donc0xffffffff
pas pour la portabilité ou l'exactitude, mais pour la clarté.-1
reste une solution portable et rétrocompatible pour les deux langues, mais cela pourrait affecter une partie du raisonnement dans d'autres réponses.Réponses:
Je vous recommande de le faire exactement comme vous l'avez montré, car c'est le plus simple. Initialiser à
-1
qui 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'ununsigned
type.Pour un exemple de surprise possible, considérez celle-ci:
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 ununsigned int
, puis l'attribueraa
. Ce qui se passe quand il yunsigned long
a 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:
La raison en est qu'il
~0
faut inverser tous les bits. Qui donnera à inverser-1
sur 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'initialisea
à 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-1
ouUINT_MAX
. Le second dépendra du type dea
- vous devrez utiliserULONG_MAX
pour un fichierunsigned 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
-1
tous les bits sont un (ce n'est pas toujours le cas). Et nous ne parlons pas de savoir si~0
tous les bits sont un (il en a, bien sûr).Mais ce dont nous parlons est le résultat de la
flags
variable initialisée . Et pour cela, seul-1
fonctionnera avec tous les types et machines.la source
numeric_limits<size_t>::max()
est un peu longue, mais le casting aussi ...-1
sont représentés par, ni ce que les bits~0
ont. 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~0
peut ne pas être-1
, mais c'est la valeur dont vous avez besoin. Voir ma réponse et le résumé de @ Dingo.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.
la source
~0
donne uneint
valeur avec tous les bits définis, bien sûr. Mais attribuer unint
à ununsigned int
ne 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'uneint
valeur négative à ununsigned int
produit 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.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.
la source
Une manière qui évite les problèmes mentionnés est de faire simplement:
Portable et précis.
la source
flags
commeconst
.unsigned int const flags = ~0u;
~0
est un entier dont tous les bits sont définis sur 1, mais lorsque vous l'assignez ensuiteint
à launsigned
variableflags
, vous effectuez une conversion de valeur de-2**31
(en supposant un 32 bitsint
) à(-2**31 % 2**32) == 2**31
, qui est un entier avec tous les bits sauf le premier set à 1.u
suffixe 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 (unsigned
et 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.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 car0xffffffff
suppose que unsigned int est un entier 32 bits.la source
auto
.auto const flags = std::numeric_limit<unsigned>::max()
.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,
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:
Tout cela est correct et plus évident pour vos collègues programmeurs.
Et avec C ++ 11 : nous pouvons utiliser
auto
pour rendre l'un de ceux-ci encore plus simple:Je considère comme correct et évident mieux que simplement correct.
la source
La conversion de -1 en n'importe quel type non signé est garantie par la norme pour aboutir à tout-un. L'utilisation de
~0U
est généralement mauvaise car0
a typeunsigned int
et 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,~0
devrait ê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
0xffffffff
si 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 limitesUINT_MAX
,ULONG_MAX
,ULLONG_MAX
, etc.Personnellement, j'utilise toujours -1. Cela fonctionne toujours et vous n'avez pas à y penser.
la source
~(type)0
(bien, remplissez bientype
sû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.neg
instruction. 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.var = ~(0*var)
cas échouera pourvar
être un type non signé plus étroit queint
. Peutvar = ~(0U*var)
- être ? (Personnellement, je préfère toujours-1
, cependant).Tant que vous avez l'
#include <limits.h>
un de vos includes, vous devez simplement utiliserSi vous voulez une valeur longue de bits, vous pouvez utiliser
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.
la source
Oui. Comme mentionné dans d'autres réponses,
-1
est 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:
Usage:
la source
ALL_BITS_TRUE ^ a
oùa
est 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.ALL_BITS_TRUE ^ a
donne une erreur de compilation carALL_BITS_TRUE
c'est ambigu. Il pourrait être utilisé commeuint32_t(ALL_BITS_TRUE) ^ a
, cependant. Vous pouvez l'essayer vous-même sur cpp.sh :) Aujourd'hui, j'ajouterais unstatic_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");
dans leoperator
pour être sûr que les utilisateurs n'essaient pas de l'utiliserint(ALL_BITS_TRUE)
. Je mettrai à jour la réponse.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_t
types.la source
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:
Ce qui suit met 64 1 dans RAX:
Juste pour être complet, ce qui suit met 32 1 dans la partie inférieure de RAX (aka EAX):
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:
le résultat est:
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.
la source
UINT64_C(0xffffffff)
développe en quelque chose comme0xffffffffuLL
, 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.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):
Cela laisse la possibilité pour l'un des bits d'être n'importe quoi.
la source
Bien que le
0xFFFF
(ou0xFFFFFFFF
, 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
typedef
valeur 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 unint
, mais rentre dans ununsigned int
. Si anint
vaut 16 bits, alorslongvar &= ~0x4000;
oulongvar &= ~0x10000
; effacera un bit delongvar
, maislongvar &= ~0x8000;
effacera le bit 15 et tous les bits au-dessus. Les valeurs qui s'intègrentint
auront l'opérateur de complément appliqué à un typeint
, mais le résultat sera étendu au signelong
, définissant les bits supérieurs. Les valeurs qui sont trop grandes pourunsigned int
auront l'opérateur de complément appliqué au typelong
. Les valeurs comprises entre ces tailles appliqueront cependant l'opérateur de complément au typeunsigned int
, qui sera ensuite converti en typelong
sans extension de signe.la source
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.
la source
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:
Celui-ci contiendra toujours la quantité exacte de bits dont vous avez besoin. Il construit un
std::bitset
avec tous les bits mis à 1 pour les mêmes raisons mentionnées dans d'autres réponses.la source
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.la source
Je dis:
Cela vous donnera toujours le résultat souhaité.
la source
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:
<limits>
et utiliserstd::numeric_limits< your_type >::max()
L'objectif pourrait être d'ajouter plus de clarté, car l'attribution
-1
nécessiterait toujours des commentaires explicatifs.la source
Un moyen de rendre le sens un peu plus évident et pourtant d'éviter de répéter le type:
la source
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() {
}
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
la source