Ich habe gesehen, dass dieses Muster in C & C ++ häufig verwendet wird.
unsigned int flags = -1; // all bits are true
Ist dies ein guter tragbarer Weg, um dies zu erreichen? Oder benutzt 0xffffffff
oder ~0
besser?
c++
c
binary
bit-fields
hyperlogisch
quelle
quelle
-1
immer funktioniert, zeigt die Tatsache, dass ein Kommentar benötigt wird, nachdem er angezeigt wurde, dass es sich nicht um klaren Code handelt. Wenn die Variable eine Sammlung von Flags sein soll, warum sollte ihr dann eine Ganzzahl zugewiesen werden? Sein Typ kann eine ganze Zahl sein, aber es ist sicherlich nicht semantisch eine ganze Zahl. Sie werden es niemals erhöhen oder multiplizieren. Ich würde es also0xffffffff
nicht aus Gründen der Portabilität oder Korrektheit verwenden, sondern aus Gründen der Klarheit.-1
eine tragbare und abwärtskompatible Lösung für beide Sprachen handelt, kann jedoch einige der Überlegungen in anderen Antworten beeinflussen.Antworten:
Ich empfehle Ihnen, es genau so zu machen, wie Sie es gezeigt haben, da es das einfachste ist. Die Initialisierung
-1
funktioniert immer , unabhängig von der tatsächlichen Vorzeichendarstellung, während~
sie manchmal ein überraschendes Verhalten aufweist, da Sie den richtigen Operandentyp haben müssen. Nur dann erhalten Sie den höchsten Wert einesunsigned
Typs.Betrachten Sie als Beispiel für eine mögliche Überraschung Folgendes:
Es wird nicht unbedingt ein Muster mit allen Bits 1 gespeichert
a
. Es wird jedoch zuerst ein Muster mit allen Bits 1 in einem erstelltunsigned int
und dann zugewiesena
. Was passiert, wennunsigned long
mehr Bits vorhanden sind, ist, dass nicht alle davon 1 sind.Und betrachten Sie diese, die bei der Komplementdarstellung eines Nicht-Zwei fehlschlägt:
Der Grund dafür ist, dass
~0
alle Bits invertiert werden müssen. Invertierung dass Ausbeute wird-1
auf eine Ergänzung Maschine zwei (was der Wert ist , die wir brauchen!), Wird aber nicht nachgeben-1
auf eine andere Darstellung. Auf der eigenen Komplementmaschine ergibt sich Null. Auf einer Komplementmaschine wird das Obigea
auf Null initialisiert .Das, was Sie verstehen sollten, ist, dass es nur um Werte geht - nicht um Bits. Die Variable wird mit einem Wert initialisiert . Wenn Sie im Initialisierer die Bits der für die Initialisierung verwendeten Variablen ändern, wird der Wert gemäß diesen Bits generiert. Der Wert, den Sie benötigen, um
a
auf den höchstmöglichen Wert zu initialisieren , ist-1
oderUINT_MAX
. Die zweite hängt von der Art aba
- Sie müssenULONG_MAX
für eine verwendenunsigned long
. Der erste hängt jedoch nicht von seinem Typ ab, und es ist eine gute Möglichkeit, den höchsten Wert zu erzielen.Wir sprechen nicht darüber, ob
-1
alle Bits eins sind (es hat nicht immer). Und wir reden nicht darüber, ob~0
alle Bits eins sind (es hat natürlich).Wir sprechen jedoch über das Ergebnis der initialisierten
flags
Variablen. Und für sie, nur-1
mit jeder Art und Maschine arbeiten.quelle
numeric_limits<size_t>::max()
ist etwas langatmig, aber auch die Besetzung ...-1
dargestellt wird, noch wird gefragt, welche Bits vorhanden~0
sind. Werte interessieren uns vielleicht nicht, aber der Compiler tut es. Wir können die Tatsache nicht ignorieren, dass Operationen mit und nach Werten arbeiten. Der Wert von ist~0
möglicherweise nicht-1
, aber dies ist der Wert, den Sie benötigen. Siehe meine Antwort und die Zusammenfassung von @ Dingo.unsigned int flags = -1;
ist tragbar.unsigned int flags = ~0;
ist nicht portabel, da es auf einer Zwei-Komplement-Darstellung beruht.unsigned int flags = 0xffffffff;
ist nicht portabel, da 32-Bit-Ints angenommen werden.Wenn Sie alle Bits so einstellen möchten, wie es der C-Standard garantiert, verwenden Sie das erste.
quelle
~0
ergibtint
natürlich einen Wert mit allen gesetzten Bits. Das Zuweisen von aint
zu a führtunsigned int
jedoch nicht unbedingt dazu, dass das vorzeichenlose int dasselbe Bitmuster wie das vorzeichenbehaftete Bitmuster aufweist. Nur bei einer 2er-Komplementdarstellung ist dies immer der Fall. Bei einer 1s-Komplement- oder Vorzeichengrößen-Darstellung führt das Zuweisen eines negativenint
Werts zuunsigned int
einem anderen Bitmuster. Dies liegt daran, dass der C ++ - Standard die vorzeichenbehaftete -> vorzeichenlose Konvertierung als den modulo-gleichen Wert definiert, nicht als den Wert mit denselben Bits.Ehrlich gesagt denke ich, dass alle FFFs besser lesbar sind. In Bezug auf den Kommentar, dass es sich um ein Antimuster handelt, würde ich argumentieren, dass Sie sich wahrscheinlich in einer Situation befinden, in der Sie sich sowieso für die Größe der Variablen interessieren, was so etwas wie Boost erfordern würde, wenn Sie sich wirklich darum kümmern, dass alle Bits gesetzt / gelöscht werden :: uint16_t usw.
quelle
Ein Weg, um die genannten Probleme zu vermeiden, besteht darin, einfach Folgendes zu tun:
Tragbar und auf den Punkt.
quelle
flags
als zu deklarierenconst
.unsigned int const flags = ~0u;
~0
handelt es sich beispielsweise um eine Ganzzahl, bei der alle Bits auf 1 gesetzt sind. Wenn Sie diesint
jedoch derunsigned
Variablen zuweisenflags
, führen Sie eine Wertekonvertierung von-2**31
(unter der Annahme eines 32-Bitint
) in(-2**31 % 2**32) == 2**31
eine Ganzzahl durch mit allen Bits außer dem ersten Satz auf 1.u
Suffix in Ihrer Antwort nicht bemerkt . Das würde natürlich funktionieren, hat aber immer noch das Problem, den von Ihnen verwendeten (unsigned
und nicht größeren) Datentyp zweimal anzugeben , was zu Fehlern führen kann. Der Fehler tritt jedoch höchstwahrscheinlich auf, wenn die Zuweisung und die anfängliche Variablendeklaration weiter voneinander entfernt sind.Ich bin mir nicht sicher, ob die Verwendung eines vorzeichenlosen int für Flags in C ++ überhaupt eine gute Idee ist. Was ist mit Bitset und dergleichen?
std::numeric_limit<unsigned int>::max()
ist besser, da0xffffffff
davon ausgegangen wird, dass int ohne Vorzeichen eine 32-Bit-Ganzzahl ist.quelle
auto
.auto const flags = std::numeric_limit<unsigned>::max()
.Tragbar? Ja .
Gut? Umstritten , wie die Verwirrung in diesem Thread zeigt. Klar genug zu sein, dass Ihre Programmierkollegen den Code ohne Verwirrung verstehen können, sollte eine der Dimensionen sein, die wir für guten Code messen.
Diese Methode ist auch anfällig für Compiler-Warnungen . Um die Warnung zu umgehen, ohne Ihren Compiler zu lähmen, benötigen Sie eine explizite Besetzung. Beispielsweise,
Die explizite Besetzung erfordert, dass Sie auf den Zieltyp achten. Wenn Sie auf den Zieltyp achten, vermeiden Sie natürlich die Fallstricke der anderen Ansätze.
Mein Rat wäre, auf den Zieltyp zu achten und sicherzustellen, dass keine impliziten Conversions vorhanden sind. Beispielsweise:
All dies ist korrekt und für Ihre Programmierkollegen offensichtlicher .
Und mit C ++ 11 : Wir können verwenden
auto
, um all dies noch einfacher zu machen:Ich halte richtig und offensichtlich für besser als einfach richtig.
quelle
Die Konvertierung von -1 in einen vorzeichenlosen Typ wird vom Standard garantiert, um alle zu ergeben. Die Verwendung von
~0U
ist im Allgemeinen schlecht, da sie0
einen Typ hatunsigned int
und nicht alle Bits eines größeren vorzeichenlosen Typs ausfüllt, es sei denn, Sie schreiben explizit so etwas~0ULL
. Auf vernünftigen Systemen~0
sollte identisch sein mit-1
, aber da der Standard Ein-Komplement- und Vorzeichen- / Größen-Darstellungen erlaubt, ist er streng genommen nicht portabel.Natürlich ist es immer in Ordnung zu schreiben,
0xffffffff
wenn Sie wissen, dass Sie genau 32 Bit benötigen, aber -1 hat den Vorteil, dass es in jedem Kontext funktioniert, auch wenn Sie die Größe des Typs nicht kennen, z. B. Makros, die für mehrere Typen funktionieren oder wenn die Größe des Typs je nach Implementierung variiert. Wenn Sie die Art wissen, eine andere sichere Weise alle-onen erhalten die Grenze Makros istUINT_MAX
,ULONG_MAX
,ULLONG_MAX
etc.Persönlich benutze ich immer -1. Es funktioniert immer und Sie müssen nicht darüber nachdenken.
quelle
~(type)0
( naja,type
natürlich rechts ausfüllen ). Das Wirken von Null führt immer noch zu einer Null, das ist also klar, und das Negieren aller Bits im Zieltyp ist ziemlich klar definiert. Es ist jedoch nicht so oft, dass ich diese Operation tatsächlich möchte. YMMV.neg
Anweisung. Maschinen mit falsch vorzeichenbehaftetem arithmetischem Verhalten haben separate vorzeichenbehaftete / vorzeichenlose arithmetische Opcodes. Natürlich würde ein wirklich guter Compiler die signierten Opcodes auch für signierte Werte immer ignorieren und dadurch zwei Zweierkomplemente kostenlos erhalten.var = ~(0*var)
Fall schlägt fehl, weilvar
er ein vorzeichenloser Typ ist, der schmaler als istint
. Vielleichtvar = ~(0U*var)
? (Ich persönlich bevorzuge es-1
aber immer noch).Solange Sie
#include <limits.h>
eines Ihrer Includes haben, sollten Sie nur verwendenWenn Sie ein langes Stück Bits wollen, können Sie verwenden
Bei diesen Werten werden garantiert alle Wertbits der Ergebnismenge auf 1 gesetzt, unabhängig davon, wie vorzeichenbehaftete Ganzzahlen implementiert sind.
quelle
Ja. Wie in anderen Antworten erwähnt,
-1
ist es am tragbarsten; Es ist jedoch nicht sehr semantisch und löst Compiler-Warnungen aus.Versuchen Sie diesen einfachen Helfer, um diese Probleme zu lösen:
Verwendung:
quelle
ALL_BITS_TRUE ^ a
woa
eine Ganzzahl mit Vorzeichen? Der Typ bleibt eine Ganzzahl mit Vorzeichen und das Bitmuster (Objektdarstellung) hängt davon ab, ob das Ziel das Komplement von 2 ist oder nicht.ALL_BITS_TRUE ^ a
gibt einen Kompilierungsfehler aus, da dieser nichtALL_BITS_TRUE
eindeutig ist. Es könnte jedoch wie verwendetuint32_t(ALL_BITS_TRUE) ^ a
werden. Sie können es selbst auf cpp.sh versuchen :) Heutzutage würde ich einstatic_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");
in das hinzufügenoperator
, um sicherzugehen, dass Benutzer nicht versuchen, es zu verwendenint(ALL_BITS_TRUE)
. Ich werde die Antwort aktualisieren.Ich würde das -1-Ding nicht machen. Es ist eher nicht intuitiv (zumindest für mich). Das Zuweisen signierter Daten zu einer nicht signierten Variablen scheint nur eine Verletzung der natürlichen Ordnung der Dinge zu sein.
In deiner Situation benutze ich immer
0xFFFF
. (Verwenden Sie natürlich die richtige Anzahl von Fs für die variable Größe.)[Übrigens sehe ich den -1-Trick sehr selten im realen Code.]
Außerdem, wenn Sie wirklich über die einzelnen Bits in einem vairable kümmern, wäre es sinnvoll sein , die mit fester Breite zu beginnen
uint8_t
,uint16_t
,uint32_t
Typen.quelle
Auf Intels IA-32-Prozessoren ist es in Ordnung, 0xFFFFFFFF in ein 64-Bit-Register zu schreiben und die erwarteten Ergebnisse zu erhalten. Dies liegt daran, dass IA32e (die 64-Bit-Erweiterung zu IA32) nur 32-Bit-Sofortnachrichten unterstützt. In 64-Bit-Anweisungen werden 32-Bit -Sofortnachrichten auf 64-Bit vorzeichenerweitert .
Folgendes ist illegal:
Im Folgenden werden 64 1s in RAX eingefügt:
Der Vollständigkeit halber werden im Folgenden 32 1s in den unteren Teil von RAX (auch bekannt als EAX) eingefügt:
Tatsächlich sind Programme fehlgeschlagen, als ich 0xffffffff in eine 64-Bit-Variable schreiben wollte, und stattdessen habe ich 0xffffffffffffffff erhalten. In C wäre dies:
Das Ergebnis ist:
Ich dachte, dies als Kommentar zu allen Antworten zu posten, die besagten, dass 0xFFFFFFFF 32 Bit annimmt, aber so viele Leute antworteten, dass ich dachte, ich würde es als separate Antwort hinzufügen.
quelle
UINT64_C(0xffffffff)
erweitert sich auf so etwas wie0xffffffffuLL
, ist es definitiv ein Compiler-Fehler. Der C-Standard behandelt weitgehend Werte , der durch dargestellte Wert0xffffffff
ist 4294967295 (nicht 36893488147419103231), und es sind keine Konvertierungen in vorzeichenbehaftete Ganzzahltypen in Sicht.In der Antwort von litb finden Sie eine sehr klare Erklärung der Probleme.
Mein Widerspruch ist, dass es streng genommen keine Garantien für beide Fälle gibt. Ich kenne keine Architektur, die keinen vorzeichenlosen Wert von "eins weniger als zwei hoch der Anzahl der Bits" darstellt, da alle Bits gesetzt sind, aber hier ist, was der Standard tatsächlich sagt (3.9.1 / 7 plus) Anmerkung 44):
Das lässt die Möglichkeit, dass eines der Bits überhaupt etwas ist.
quelle
Obwohl das
0xFFFF
(oder0xFFFFFFFF
usw.) möglicherweise leichter zu lesen ist, kann es die Portabilität von Code beeinträchtigen, der ansonsten portabel wäre. Stellen Sie sich zum Beispiel eine Bibliotheksroutine vor, um zu zählen, wie viele Elemente in einer Datenstruktur bestimmte Bits gesetzt haben (die genauen Bits werden vom Aufrufer angegeben). Die Routine kann völlig unabhängig davon sein, was die Bits darstellen, muss aber immer noch eine Konstante "Alle Bits gesetzt" haben. In einem solchen Fall ist -1 weitaus besser als eine Hex-Konstante, da es mit jeder Bitgröße funktioniert.Die andere Möglichkeit, wenn ein
typedef
Wert für die Bitmaske verwendet wird, wäre die Verwendung von ~ (bitMaskType) 0; wenn Bitmaske nur geschieht , um eine 16-Bit - Typ zu sein, wird dieser Ausdruck nur 16 Bits (auch wenn ‚int‘ sonst 32 Bits sein würde) , aber da 16 Bits alle sein , die erforderlich sind, sollten die Dinge in Ordnung werden zur Verfügung gestellt , dass man verwendet tatsächlich den entsprechenden Typ in der Typumwandlung.Im Übrigen haben Ausdrücke der Form
longvar &= ~[hex_constant]
ein unangenehmes Problem, wenn die Hex-Konstante zu groß ist, um in eine zu passenint
, aber in eine passtunsigned int
. Wenn anint
16 Bit ist, dannlongvar &= ~0x4000;
oderlongvar &= ~0x10000
; löscht ein Bit vonlongvar
,longvar &= ~0x8000;
löscht jedoch Bit 15 und alle darüber liegenden Bits. Bei Werten, die passen,int
wird der Komplementoperator auf einen Typ angewendetint
, aber das Ergebnis wird mit einem Vorzeichen erweitertlong
, wobei die oberen Bits gesetzt werden. Bei Werten, für die zu groß ist,unsigned int
wird der Komplementoperator auf den Typ angewendetlong
. Werte zwischen diesen Größen wenden jedoch den Komplementoperator auf type anunsigned int
, der dannlong
ohne Vorzeichenerweiterung in type konvertiert wird .quelle
Praktisch: Ja
Theoretisch: Nein.
-1 = 0xFFFFFFFF (oder welche Größe auch immer ein Int auf Ihrer Plattform hat) gilt nur für die Zweierkomplementarithmetik. In der Praxis wird es funktionieren, aber es gibt ältere Maschinen (IBM-Mainframes usw.), auf denen Sie ein tatsächliches Vorzeichenbit anstelle einer Zweierkomplementdarstellung haben. Ihre vorgeschlagene ~ 0-Lösung sollte überall funktionieren.
quelle
Wie andere bereits erwähnt haben, ist -1 der richtige Weg, um eine Ganzzahl zu erstellen, die in einen vorzeichenlosen Typ konvertiert wird, wobei alle Bits auf 1 gesetzt sind. Das Wichtigste in C ++ ist jedoch die Verwendung korrekter Typen. Daher lautet die richtige Antwort auf Ihr Problem (einschließlich der Antwort auf die von Ihnen gestellte Frage):
Dies enthält immer genau die Anzahl der benötigten Bits. Es konstruiert a,
std::bitset
wobei alle Bits aus den gleichen Gründen wie in anderen Antworten auf 1 gesetzt sind.quelle
Es ist sicherlich sicher, da bei -1 immer alle verfügbaren Bits gesetzt sind, aber ~ 0 gefällt mir besser. -1 macht für einen einfach nicht viel Sinn
unsigned int
.0xFF
... ist nicht gut, weil es von der Breite des Typs abhängt.quelle
Ich sage:
Dadurch erhalten Sie immer das gewünschte Ergebnis.
quelle
Die Nutzung der Tatsache, dass das Zuweisen aller Bits zu einem für einen vorzeichenlosen Typ eins gleichbedeutend damit ist, den maximal möglichen Wert für den angegebenen Typ zu nehmen
und den Umfang der Frage auf alle vorzeichenlosen Ganzzahltypen auszudehnen :
Das Zuweisen von -1 funktioniert für alle vorzeichenlosen Ganzzahltyp (vorzeichenloses int, uint8_t, uint16_t usw.) sowohl für C als auch für C ++.
Alternativ können Sie für C ++ entweder:
<limits>
und verwendenstd::numeric_limits< your_type >::max()
Der Zweck könnte mehr Klarheit schaffen, da die Zuweisung
-1
immer einen erklärenden Kommentar erfordern würde.quelle
Eine Möglichkeit, die Bedeutung etwas deutlicher zu machen und dennoch zu vermeiden, dass der Typ wiederholt wird:
quelle
Ja, die gezeigte Darstellung ist sehr korrekt, als ob wir es umgekehrt machen würden. u erfordert einen Operator, der alle Bits umkehrt. In diesem Fall ist die Logik jedoch recht einfach, wenn wir die Größe der ganzen Zahlen in der Maschine berücksichtigen
In den meisten Maschinen beträgt eine Ganzzahl beispielsweise 2 Byte = 16 Bit. Der maximale Wert, den sie enthalten kann, beträgt 2 ^ 16-1 = 65535 2 ^ 16 = 65536
0% 65536 = 0 -1% 65536 = 65535, was 1111 ............. 1 entspricht, und alle Bits werden auf 1 gesetzt (wenn wir die Restklassen mod 65536 berücksichtigen), daher ist es viel geradeaus.
ich vermute
Nein, wenn Sie diese Vorstellung berücksichtigen, ist sie perfekt für vorzeichenlose Ints geeignet und funktioniert tatsächlich
Überprüfen Sie einfach das folgende Programmfragment
int main () {
}}
Antwort für b = 4294967295, wobei -1% 2 ^ 32 auf 4-Byte-Ganzzahlen ist
Daher ist es für vorzeichenlose Ganzzahlen vollkommen gültig
im Falle von Unstimmigkeiten bitte melden
quelle