Question

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’y parvenir ?Ou utilise 0xffffffff ou ~0 mieux?

Était-ce utile?

La solution

Je vous recommande de le faire exactement comme vous l’avez montré, car c’est le plus simple. Initialisez sur -1 ce qui fonctionnera toujours , indépendamment de la représentation du signe, alors 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 type unsigned.

Pour un exemple de surprise possible, considérons celui-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’assignera à unsigned long. Qu'est-ce qui se passe quand ~0 a plus de bits, c'est que tous ne sont pas 1.

Et considérez celui-ci, qui échouera sur la représentation du complément d'un non-deux:

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

La raison en est que UINT_MAX doit inverser tous les bits. Inverser cela produira ULONG_MAX sur une machine à complément à deux (ce qui est la valeur dont nous avons besoin!), Mais ne ne produira pas flags sur une autre représentation. Sur une machine complémentaire, elle donne zéro. Ainsi, sur la machine du complément de l'un, ce qui précède initialisera <=> à 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 une valeur . Si vous modifiez dans l'initialiseur 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 <=> à la valeur la plus élevée possible est <=> ou <=>. La seconde dépendra du type de <=> - vous devrez utiliser <=> pour un <=>. 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 discutons pas de savoir si <=> a tous les bits un (ce n'est pas toujours le cas). Et nous ne ne parlons pas de savoir si <=> contient tous les bits un (bien entendu).

Mais nous parlons du résultat de la variable <=> initialisée. Et pour cela, seulement <=> fonctionnera avec tous les types et toutes les machines.

Autres conseils

  • unsigned int flags = -1; est portable.
  • unsigned int flags = ~0; n'est pas portable car il repose sur une représentation en complément à deux.
  • unsigned int flags = 0xffffffff; n'est pas portable car cela suppose des ints 32 bits.

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

Franchement, je pense que tous les fff sont plus lisibles. En ce qui concerne le commentaire selon lequel il s'agit d'un antipattern, si vous tenez vraiment à ce que tous les bits soient définis / effacés, je dirais que vous êtes probablement dans une situation où vous vous souciez de toute façon de la taille de la variable, ce qui nécessiterait quelque chose comme boost. :: uint16_t, etc.

Pour éviter les problèmes mentionnés, il suffit de faire:

unsigned int flags = 0;
flags = ~flags;

Portable et droit au but.

Je ne suis pas sûr d'utiliser un int non signé pour les indicateurs est une bonne idée en premier lieu en C ++. Qu'en est-il des bitset etc.?

std::numeric_limit<unsigned int>::max() est préférable car 0xffffffff suppose que unsigned int est un entier 32 bits.

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

& "Est-ce un bon moyen [,] portable pour y parvenir? &";

Portable? Oui .

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

De plus, cette méthode est sujette aux avertissements du compilateur . Pour éviter cet avertissement sans endommager votre compilateur, vous avez besoin d'une distribution explicite. Par exemple,

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

La distribution explicite nécessite que vous accordiez une attention particulière au type de cible. Si vous prêtez 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 vous 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);

Toutes ces réponses sont correctes et plus évidentes pour vos collègues programmeurs.

Et avec C ++ 11 : nous pouvons utiliser auto pour simplifier ces opérations:

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 correct et évident mieux que simplement correct.

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

Bien sûr, il est toujours correct d'écrire 0xffffffff si vous savez que vous avez besoin d'exactement 32 bits, mais -1 a l'avantage de fonctionner dans tous les contextes même si vous ne connaissez pas la taille du type, tels que les macros qui fonctionnent sur plusieurs types, ou si la taille du type varie selon l'implémentation. Si vous connaissez le type, un autre moyen sûr d'obtenir des unités tout-en-un est d'utiliser les macros de limite UINT_MAX, ULONG_MAX, ULLONG_MAX, etc.

.

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

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:

static const struct All1s
{
    template<typename UnsignedType>
    inline operator UnsignedType(void) const
    {
        return static_cast<UnsignedType>(-1);
    }
} ALL_BITS_TRUE;

Utilisation:

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;

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

unsigned int flags = UINT_MAX;

Si vous voulez une longue quantité de bits, vous pouvez utiliser

unsigned long flags = ULONG_MAX;

Il est garanti que ces valeurs ont tous les bits de valeur du résultat mis à 1, quelle que soit la manière dont les entiers signés sont implémentés.

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

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

[BTW, je vois très rarement le truc -1 en code réel.]

De plus, si vous vous souciez vraiment des bits individuels d'un vairable, il serait judicieux de commencer à utiliser les types uint8_t, uint16_t, uint32_t, <->, <->,

de largeur fixe.

Sur les processeurs Intel IA-32, vous pouvez écrire 0xFFFFFFFF dans un registre 64 bits et obtenir les résultats attendus.En effet, IA32e (l'extension 64 bits de IA32) ne prend en charge que les fichiers immédiats 32 bits.Dans les instructions 64 bits, les immédiates 32 bits sont signe étendu à 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 (alias EAX) :

mov eax, 0ffffffffh

Et en fait, des programmes ont échoué lorsque je voulais écrire 0xffffffff dans une variable 64 bits et 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 commentaire à toutes les réponses disant que 0xFFFFFFFF suppose 32 bits, mais tant de gens y ont répondu que j'ai pensé que je l'ajouterais comme réponse distincte.

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

Mon désaccord est que, très strictement, il n'y a aucune garantie pour les deux cas. Je ne connais aucune architecture qui ne représente pas une valeur non signée de "un 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:] Représentation positionnelle des nombres entiers utilisant les chiffres binaires 0 et 1, dans laquelle les valeurs représentées par les bits successifs sont additives, commencent par 1 et sont multipliées par une puissance entière successive de 2, sauf peut-être pour le bit avec la position la plus haute.

Cela laisse la possibilité pour l'un des bits d'être quoi que ce soit.

Pratiquement: oui

Théoriquement: non.

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

Bien que la lecture 0xFFFF (ou 0xFFFFFFFF, etc.) puisse être plus facile, elle peut empêcher la portabilité dans du code qui, autrement, serait portable. Considérons, par exemple, une routine de bibliothèque qui compte le nombre d'éléments d'une structure de données pour lesquels certains bits sont définis (les bits exacts étant spécifiés par l'appelant). La routine peut être totalement agnostique quant à ce que les bits représentent, mais il faut quand même avoir un & "Tous les bits définis &"; constant. Dans ce cas, -1 sera nettement meilleur qu’une constante hexagonale, car il fonctionnera avec n’importe quelle taille de bit.

L’autre possibilité, si une valeur typedef est utilisée pour le masque binaire, serait d’utiliser ~ (bitMaskType) 0; si le masque de bits n'est que de type 16 bits, cette expression n'aura que 16 bits (même si «int» aurait 32 bits), mais comme 16 bits seront tout ce qui est requis, les choses devraient bien se passer à condition que l'on utilise réellement le type approprié dans la conversion de type.

Incidemment, les expressions de la forme longvar &= ~[hex_constant] ont un vilain piège si la constante hexagonale est trop grande pour tenir dans un int, mais tiendra dans un unsigned int. Si un longvar &= ~0x4000; est de 16 bits, alors longvar &= ~0x10000 ou longvar; effacera un bit de longvar &= ~0x8000;, mais long effacera le bit 15 et tous les bits supérieurs. L'opérateur de complément est appliqué à un type <=> pour les valeurs qui correspondent à <=>, mais le résultat sera étendu au signe <=>, en définissant les bits supérieurs. Pour les valeurs trop grandes pour <=>, l'opérateur de complément sera appliqué au type <=>. Les valeurs comprises entre ces tailles appliqueront toutefois l'opérateur du complément au type <=>, qui sera ensuite converti au type <=> sans extension de signe.

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

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

Cela contiendra toujours le nombre exact de bits dont vous avez besoin. Il construit un std::bitset avec tous les bits mis à 1 pour les mêmes raisons que celles mentionnées dans d'autres réponses.

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

Je dis:

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

Cela vous donnera toujours le résultat souhaité.

S'appuyant sur le fait qu'affecter tous les bits à un pour un type non signé équivaut à prendre la valeur maximale possible pour le type donné,
et étendre le champ de la question à tous les types entiers unsigned :

Assigner -1 fonctionne pour tout type d'entier unsigned (unsigned int, uint8_t, uint16_t , etc.) pour C et C ++.

En guise d'alternative, pour C ++, vous pouvez soit:

  1. Inclure <limits> et utiliser std::numeric_limits< your_type >::max()
  2. Ecrivez une fonction personnalisée basée sur un modèle (elle permettrait également un contrôle de cohérence, c'est-à-dire si le type de destination est vraiment un type non signé. type)

Le but pourrait être d'ajouter plus de clarté, car l'attribution -1 aurait toujours besoin de commentaires explicatifs.

Une façon de rendre le sens plus évident tout en évitant de répéter le type:

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

oui, la représentation affichée est très correcte, car si vous le faisiez dans l'autre sens, il faudrait qu'un opérateur inverse 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 est égal à 2 octets = 16 bits. La valeur maximale qu'il peut contenir est égal à 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) par conséquent, il est beaucoup plus simple.

Je suppose

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

il suffit de vérifier 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 qui vaut -1% 2 ^ 32 sur un entier de 4 octets

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

en cas de divergence, rapport plzz

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top