Ist es sicher, -1 zu verwenden, um alle Bits auf true zu setzen?

132

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 0xffffffffoder ~0besser?

hyperlogisch
quelle
1
Ich verstehe es nicht, kannst du es bitte erklären?
Corazza
8
Ich denke, ob die Bedeutung des Codes klar ist, ist die wichtigste Frage. Auch wenn dies -1immer 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 also 0xffffffffnicht aus Gründen der Portabilität oder Korrektheit verwenden, sondern aus Gründen der Klarheit.
Cam Jackson
@CamJackson ist der Kommentar nicht und jeder, der C-Code schreibt, könnte mit der Darstellung von Werten vertraut sein.
Miles Rout
Die Frage wurde ursprünglich ordnungsgemäß als C und C ++ markiert. Die Sprachen können darin abweichen, dass C ++ einen Vorschlag hat, das Zweierkomplement zu verlangen. Dies ändert jedoch nichts an der Tatsache, dass es sich um -1eine tragbare und abwärtskompatible Lösung für beide Sprachen handelt, kann jedoch einige der Überlegungen in anderen Antworten beeinflussen.
Adrian McCarthy

Antworten:

154

Ich empfehle Ihnen, es genau so zu machen, wie Sie es gezeigt haben, da es das einfachste ist. Die Initialisierung -1funktioniert 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 eines unsignedTyps.

Betrachten Sie als Beispiel für eine mögliche Überraschung Folgendes:

unsigned long a = ~0u;

Es wird nicht unbedingt ein Muster mit allen Bits 1 gespeichert a. Es wird jedoch zuerst ein Muster mit allen Bits 1 in einem erstellt unsigned intund dann zugewiesen a. Was passiert, wenn unsigned longmehr Bits vorhanden sind, ist, dass nicht alle davon 1 sind.

Und betrachten Sie diese, die bei der Komplementdarstellung eines Nicht-Zwei fehlschlägt:

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

Der Grund dafür ist, dass ~0alle Bits invertiert werden müssen. Invertierung dass Ausbeute wird -1auf eine Ergänzung Maschine zwei (was der Wert ist , die wir brauchen!), Wird aber nicht nachgeben -1auf eine andere Darstellung. Auf der eigenen Komplementmaschine ergibt sich Null. Auf einer Komplementmaschine wird das Obige aauf 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 aauf den höchstmöglichen Wert zu initialisieren , ist -1oder UINT_MAX. Die zweite hängt von der Art ab a- Sie müssen ULONG_MAXfür eine verwenden unsigned 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 -1alle Bits eins sind (es hat nicht immer). Und wir reden nicht darüber, ob ~0alle Bits eins sind (es hat natürlich).

Wir sprechen jedoch über das Ergebnis der initialisierten flagsVariablen. Und für sie, nur-1 mit jeder Art und Maschine arbeiten.

Johannes Schaub - litb
quelle
9
Warum wird -1 garantiert in alle konvertiert? Ist das durch den Standard garantiert?
Jalf
9
Die Konvertierung besteht darin, dass wiederholt eine mehr als ULONG_MAX hinzugefügt wird, bis der Bereich erreicht ist (6.3.1.3 im C TC2-Entwurf). In C ++ ist es dasselbe, nur mit einer anderen Art der Formalisierung (Modulo 2 ^ n). Es kommt alles auf mathematische Beziehungen an.
Johannes Schaub - litb
6
@litb: Casting -1 ist sicherlich ein guter Weg, um maximale vorzeichenlose Werte zu erhalten, aber es ist nicht wirklich beschreibend; das ist der Grund, warum die _MAX-Konstanten existieren (SIZE_MAX wurde in C99 hinzugefügt); Zugegeben, die C ++ - Version numeric_limits<size_t>::max()ist etwas langatmig, aber auch die Besetzung ...
Christoph
11
"Wir reden nicht darüber, ob -1 alle Bits eins hat (es hat nicht immer). Und wir reden nicht darüber, ob ~ 0 alle Bits eins hat (es hat natürlich)." - was ??? Ich dachte , der ganze Punkt war , alle Bits auf 1 gesetzt Das ist , wie Flaggen work..no ?? Du siehst dir die Teile an . Wen interessiert der Wert?
Mpen
14
@ Mark der Fragesteller kümmert sich. Er fragt: "Ist es sicher, -1 zu verwenden, um alle Bits auf true zu setzen?" Dabei wird weder gefragt, durch welche Bits -1dargestellt wird, noch wird gefragt, welche Bits vorhanden ~0sind. 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 ~0möglicherweise nicht -1, aber dies ist der Wert, den Sie benötigen. Siehe meine Antwort und die Zusammenfassung von @ Dingo.
Johannes Schaub - litb
49
  • 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.

Dingo
quelle
10
Wie beruht ~ 0 (dh der Komplementoperator des einen) auf der Komplementdarstellung von zwei?
Drew Hall
11
Du hast das rückwärts. Die Einstellung markiert -1, was auf einer Zweierkomplementdarstellung beruht. In einer Vorzeichen + Betragsdarstellung minus eins sind nur zwei Bits gesetzt: das Vorzeichenbit und das niedrigstwertige Bit der Größe.
Stephen C. Steel
15
Der C-Standard verlangt, dass der int-Wert von Null sein Vorzeichenbit hat und alle Wertbits Null sind. Nach dem eigenen Komplement sind alle diese Bits eins. Die Werte eines int mit allen gesetzten Bits sind: Vorzeichen und Größe: INT_MIN Eins-Komplement: -0 Zwei-Komplement: -1 Die Anweisung "unsigned int flags = ~ 0;" weist den oben genannten Wert zu, der der Ganzzahldarstellung der Plattform entspricht. Das '-1' des Zweierkomplements ist jedoch das einzige, das alle Flags-Bits auf eins setzt.
Dingo
9
@ Stephen: Einverstanden über die Darstellung. Wenn jedoch ein int-Wert einem vorzeichenlosen int zugewiesen wird, erhält der vorzeichenlose Wert seinen Wert nicht, indem er die interne Darstellung des int-Werts übernimmt (außer in Zweierkomplementsystemen, in denen dies im Allgemeinen funktioniert). Alle Werte, die vorzeichenlosen Ints zugewiesen sind, sind Modulo (UINT_MAX + 1), sodass die Zuweisung von -1 unabhängig von der internen Darstellung funktioniert.
Dingo
20
@ Mark: Sie verwechseln zwei Operationen. ~0ergibt intnatürlich einen Wert mit allen gesetzten Bits. Das Zuweisen von a intzu a führt unsigned intjedoch 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 negativen intWerts zu unsigned inteinem 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.
Steve Jessop
25

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.

Doug T.
quelle
Es gibt eine Reihe von Fällen, in denen Sie sich nicht so sehr darum kümmern, aber sie sind selten. ZB Algorithmen, die mit Datensätzen von N Bits arbeiten, indem sie in Blöcke der Größe von (vorzeichenlosen) * CHAR_BIT-Bits zerlegt werden.
MSalters
2
+1. Selbst wenn die Größe des Datentyps größer als die Anzahl der Fs ist (dh Sie haben nicht alle Bits auf true gesetzt), wissen Sie zumindest, welche Bits "sicher" sind, da Sie den Wert explizit festlegen zu verwenden "..
mpen
17

Ein Weg, um die genannten Probleme zu vermeiden, besteht darin, einfach Folgendes zu tun:

unsigned int flags = 0;
flags = ~flags;

Tragbar und auf den Punkt.

Hammar
quelle
2
Aber dann verlieren Sie die Fähigkeit, flagsals zu deklarieren const.
David Stone
1
@ DavidStoneunsigned int const flags = ~0u;
@Zoidberg '- Das funktioniert nicht auf anderen Systemen als dem Zweierkomplement. Bei einem Vorzeichengrößen-System ~0handelt es sich beispielsweise um eine Ganzzahl, bei der alle Bits auf 1 gesetzt sind. Wenn Sie dies intjedoch der unsignedVariablen zuweisen flags, führen Sie eine Wertekonvertierung von -2**31(unter der Annahme eines 32-Bit int) in (-2**31 % 2**32) == 2**31eine Ganzzahl durch mit allen Bits außer dem ersten Satz auf 1.
David Stone
Das ist auch ein Grund, warum diese Antwort gefährlich ist. Es scheint, als wäre die Antwort von @Zoidberg identisch - aber tatsächlich nicht. Als Person, die den Code liest, müsste ich jedoch darüber nachdenken, um zu verstehen, warum Sie zwei Schritte unternommen haben, um ihn zu initialisieren, und möglicherweise versucht sein, dies in einen einzigen Schritt zu ändern.
David Stone
2
Ah ja, ich habe das uSuffix in Ihrer Antwort nicht bemerkt . Das würde natürlich funktionieren, hat aber immer noch das Problem, den von Ihnen verwendeten ( unsignedund 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.
David Stone
13

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, da 0xffffffffdavon ausgegangen wird, dass int ohne Vorzeichen eine 32-Bit-Ganzzahl ist.

Edouard A.
quelle
Ich mag das wegen seines Standards, aber es ist zu wortreich und es bringt dich dazu, den Typ zweimal anzugeben. Die Verwendung von ~ 0 ist wahrscheinlich sicherer, da 0 ein beliebiger ganzzahliger Typ sein kann. (Obwohl ich weiß, dass es zu viel nach C. riecht)
Macke
Die Tatsache, dass es wortreich ist, kann als Vorteil angesehen werden. Aber ich mag auch ~ 0.
Edouard A.
2
Sie können die Worthaftigkeit mit dem Standardmakro UINT_MAX verringern, da Sie den Typ unsigned int ohnehin fest codieren.
2
@Macke Sie können vermeiden, den Typ in C ++ 11 mit anzugeben auto. auto const flags = std::numeric_limit<unsigned>::max().
David Stone
11
unsigned int flags = -1;  // all bits are true

"Ist dies ein guter [,] tragbarer Weg, um dies zu erreichen?"

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,

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

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:

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

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:

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

Ich halte richtig und offensichtlich für besser als einfach richtig.

Adrian McCarthy
quelle
10

Die Konvertierung von -1 in einen vorzeichenlosen Typ wird vom Standard garantiert, um alle zu ergeben. Die Verwendung von ~0Uist im Allgemeinen schlecht, da sie 0einen Typ hat unsigned intund nicht alle Bits eines größeren vorzeichenlosen Typs ausfüllt, es sei denn, Sie schreiben explizit so etwas ~0ULL. Auf vernünftigen Systemen ~0sollte 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, 0xffffffffwenn 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 ist UINT_MAX, ULONG_MAX, ULLONG_MAXetc.

Persönlich benutze ich immer -1. Es funktioniert immer und Sie müssen nicht darüber nachdenken.

R .. GitHub HÖREN SIE AUF, EIS ZU HELFEN
quelle
FWIW, wenn ich "alle 1 Bits" meine, die ich benutze ~(type)0( naja, typenatü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.
Donal Fellows
6
@Donal: du liegst einfach falsch. C gibt an, dass beim Konvertieren eines Werts, der nicht in einen vorzeichenlosen Typ passt, die Werte modulo 2 ^ n reduziert werden, wobei n die Anzahl der Bits im Zieltyp ist. Dies gilt sowohl für vorzeichenbehaftete Werte als auch für größere vorzeichenlose Typen. Es hat nichts mit Zweierkomplement zu tun.
R .. GitHub STOP HELPING ICE
2
Sie tun es die gleiche implementieren, die trivial ist; Sie verwenden nur einen vorzeichenlosen Subtraktions-Opcode anstelle eines vorzeichenbehafteten oder einer negAnweisung. 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.
R .. GitHub STOP HELPING ICE
1
@R.: Der var = ~(0*var)Fall schlägt fehl, weil varer ein vorzeichenloser Typ ist, der schmaler als ist int. Vielleicht var = ~(0U*var)? (Ich persönlich bevorzuge es -1aber immer noch).
Café
1
Diese Antwort ist viel klarer als die von Johannes Schaub. Das Zuweisen einer negativen Literal-Ganzzahl zu einem vorzeichenlosen Typ ohne Umwandlung führt jedoch normalerweise zu einer Compiler-Warnung. Das Unterdrücken der Warnung erfordert eine Besetzung, was bedeutet, dass Sie ohnehin auf den Zieltyp achten müssen. Sie können also auch UINT_MAX oder ULONG_MAX verwenden und klar sein, anstatt sich auf ein winziges Detail im Standard zu verlassen, was viele Ihrer Programmierkollegen eindeutig verwirrt .
Adrian McCarthy
5

Solange Sie #include <limits.h>eines Ihrer Includes haben, sollten Sie nur verwenden

unsigned int flags = UINT_MAX;

Wenn Sie ein langes Stück Bits wollen, können Sie verwenden

unsigned long flags = ULONG_MAX;

Bei diesen Werten werden garantiert alle Wertbits der Ergebnismenge auf 1 gesetzt, unabhängig davon, wie vorzeichenbehaftete Ganzzahlen implementiert sind.

Michael Norrish
quelle
1
Die von Ihnen vorgeschlagenen Konstanten sind tatsächlich in Limits definiert. h - stdint.h enthält die Limits für die zusätzlichen Integer-Typen (Integer mit fester Größe, intptr_t, ...)
Christoph
5

Ja. Wie in anderen Antworten erwähnt, -1ist 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:

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;

Verwendung:

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
quelle
3
Als C-Programmierer gibt mir Code wie dieser nachts schlechte Träume.
Jforberg
Und was passiert , wenn man dies in einem Kontext wie verwenden , ALL_BITS_TRUE ^ awo aeine 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.
Peter Cordes
Nein, ALL_BITS_TRUE ^ agibt einen Kompilierungsfehler aus, da dieser nicht ALL_BITS_TRUEeindeutig ist. Es könnte jedoch wie verwendet uint32_t(ALL_BITS_TRUE) ^ awerden. Sie können es selbst auf cpp.sh versuchen :) Heutzutage würde ich ein static_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");in das hinzufügen operator, um sicherzugehen, dass Benutzer nicht versuchen, es zu verwenden int(ALL_BITS_TRUE). Ich werde die Antwort aktualisieren.
Diamond Python
3

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

Myron-Semack
quelle
2

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:

mov rax, 0ffffffffffffffffh

Im Folgenden werden 64 1s in RAX eingefügt:

mov rax, 0ffffffffh

Der Vollständigkeit halber werden im Folgenden 32 1s in den unteren Teil von RAX (auch bekannt als EAX) eingefügt:

mov eax, 0ffffffffh

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:

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

Das Ergebnis ist:

x is 0xffffffffffffffff

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.

Nathan Fellman
quelle
1
Herzlichen Glückwunsch, Sie haben einen Compiler-Fehler gefunden!
tc.
Wurde dies irgendwo als Fehler dokumentiert?
Nathan Fellman
1
Angenommen, es UINT64_C(0xffffffff)erweitert sich auf so etwas wie 0xffffffffuLL, ist es definitiv ein Compiler-Fehler. Der C-Standard behandelt weitgehend Werte , der durch dargestellte Wert 0xffffffffist 4294967295 (nicht 36893488147419103231), und es sind keine Konvertierungen in vorzeichenbehaftete Ganzzahltypen in Sicht.
tc.
2

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

Die Darstellungen von Integraltypen müssen Werte unter Verwendung eines reinen binären Zahlensystems definieren. [Anmerkung 44:] Eine Positionsdarstellung für Ganzzahlen, die die Binärziffern 0 und 1 verwendet, wobei die durch aufeinanderfolgende Bits dargestellten Werte additiv sind, mit 1 beginnen und mit der aufeinanderfolgenden ganzzahligen Potenz von 2 multipliziert werden, außer vielleicht für das Bit mit die höchste Position.

Das lässt die Möglichkeit, dass eines der Bits überhaupt etwas ist.

James Hopkin
quelle
Im Allgemeinen können wir uns über den Wert der Füllbits nicht sicher sein. Und wenn wir wollen, könnten wir in Gefahr sein, da wir eine Fallendarstellung für sie erzeugen könnten (und Signale auslösen könnten). Der Standard erfordert jedoch, dass vorzeichenlose Zeichen keine Auffüllbits haben. In 4.7 / 2 des c ++ - Standards heißt es, dass beim Konvertieren einer Ganzzahl in einen vorzeichenlosen Typ der Wert der resultierenden vorzeichenlosen Variablen der kleinste Wert ist, der mit dem ganzzahligen Quellwert kongruent ist. (Modulo 2 ^ n, n == Anzahl der Bits im vorzeichenlosen Typ). dann (-1) == ((2 ^ n) -1) (mod 2 ^ n). 2 ^ n-1 hat alle Bits in einem reinen binären Nummerierungssystem gesetzt.
Johannes Schaub - Litb
Wenn wir wirklich alle Bits 1 in der Objektdarstellung eines vorzeichenlosen Typs haben möchten, benötigen wir ein Memset. Aber wir könnten dadurch eine Trap-Darstellung generieren :( Wie auch immer, eine Implementierung hat wahrscheinlich keinen Grund, ein bisschen von ihren vorzeichenlosen Ganzzahlen wegzuwerfen, damit sie ihre Werte speichert. Aber Sie haben einen sehr guten Punkt - es gibt nichts, was aufhört eine Interpretation von ein paar dummen Unsinnsbits, denke ich (abgesehen von in char / signiertem char / unsigned char, die diese nicht haben dürfen). +1 natürlich :)
Johannes Schaub - litb
Am Ende denke ich, dass der Standard klarer sein könnte, auf welche Darstellung er sich in 4.7 / 2 bezieht. Wenn es sich auf die Objektdarstellung bezieht, gibt es keinen Platz mehr zum Auffüllen von Bits (ich habe Leute gesehen, die so gestritten haben, mit denen ich nichts falsches sehe). Aber ich denke, es geht um die Wertdarstellung (weil in 4.7 / 2 sowieso alles um Werte geht - und dann können Füllbits neben den
Wertbits verschachtelt werden
1
Der Standard scheint ziemlich klar die Darstellungen von '2' s Komplement, 1 's Komplement und vorzeichenbehafteter Größe' im Auge zu haben, will aber nichts ausschließen. Interessanter Punkt zum Einfangen von Darstellungen. Soweit ich das beurteilen kann, ist das von mir zitierte Bit die Definition des "reinen binären Zahlensystems", soweit es den Standard betrifft - das "Ausnahme" -Bit am Ende ist wirklich mein einziger Zweifel, ob das Casting von -1 garantiert ist Arbeit.
James Hopkin
2

Obwohl das 0xFFFF(oder 0xFFFFFFFFusw.) 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 typedefWert 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 passen int, aber in eine passt unsigned int. Wenn an int16 Bit ist, dann longvar &= ~0x4000;oder longvar &= ~0x10000; löscht ein Bit von longvar, longvar &= ~0x8000;löscht jedoch Bit 15 und alle darüber liegenden Bits. Bei Werten, die passen, intwird der Komplementoperator auf einen Typ angewendet int, aber das Ergebnis wird mit einem Vorzeichen erweitert long, wobei die oberen Bits gesetzt werden. Bei Werten, für die zu groß ist, unsigned intwird der Komplementoperator auf den Typ angewendet long. Werte zwischen diesen Größen wenden jedoch den Komplementoperator auf type an unsigned int, der dann longohne Vorzeichenerweiterung in type konvertiert wird .

Superkatze
quelle
1

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.

Drew Hall
quelle
6
Das habe ich auch gesagt. Aber dann wurde mir klar, dass ich falsch lag, da -1 signiert immer in max_value konvertiert wird, das nach den Konvertierungsregeln nicht signiert ist, unabhängig von den Wertdarstellungen. Zumindest in C ++ habe ich den C-Standard nicht zur Hand.
Steve Jessop
6
Es gibt eine Mehrdeutigkeit. -1 ist nicht 0xFFFFFFFF. Aber -1 ist 0xFFFFFFFF, wenn es in ein vorzeichenloses int konvertiert wird (mit 32 Bit). Das macht diese Diskussion meiner Meinung nach so schwierig. Viele Leute haben sehr unterschiedliche Dinge im Sinn, wenn sie über diese Bitstrings sprechen.
Johannes Schaub - litb
1

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

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

Dies enthält immer genau die Anzahl der benötigten Bits. Es konstruiert a, std::bitsetwobei alle Bits aus den gleichen Gründen wie in anderen Antworten auf 1 gesetzt sind.

David Stone
quelle
0

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.

Zifre
quelle
4
"0xFF ... ist nicht gut, weil es von der Breite des Typs abhängt" Das ist der einzig vernünftige Weg, denke ich. Sie sollten klar definieren, was jedes Flag / Bit in Ihrem Programm bedeutet. Wenn Sie also definieren, dass Sie die niedrigsten 32 Bit zum Speichern von Flags verwenden, sollten Sie sich darauf beschränken, diese 32 Bit zu verwenden, unabhängig davon, ob die tatsächliche Größe des int 32 oder 64 beträgt.
Juan Pablo Califano
0

Ich sage:

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

Dadurch erhalten Sie immer das gewünschte Ergebnis.

Alex
quelle
4
Nicht auf Systemen mit 9-Bit-Zeichen!
tc.
0

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:

  1. Einschließen <limits>und verwendenstd::numeric_limits< your_type >::max()
  2. Schreiben Sie eine benutzerdefinierte Vorlagenfunktion (dies würde auch eine Überprüfung der Integrität ermöglichen, dh wenn der Zieltyp wirklich ein vorzeichenloser Typ ist).

Der Zweck könnte mehr Klarheit schaffen, da die Zuweisung -1immer einen erklärenden Kommentar erfordern würde.

Antonio
quelle
0

Eine Möglichkeit, die Bedeutung etwas deutlicher zu machen und dennoch zu vermeiden, dass der Typ wiederholt wird:

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

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 () {

unsigned int a=2;

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

unsigned int b=-1;

cout<<"\n"<<b;

getchar();

return 0;

}}

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

Ankit Sablok
quelle
9
Zwei Kommentare: Erstens liegen Sie absolut falsch in Bezug auf die Größe von Ganzzahlen auf „den meisten“ Computern. Zweitens ist Ihr Text sehr schwer zu lesen, weil Sie eine Summe von Seckrit-Sprachen haben. Bitte sprechen Sie uns einfach Englisch .
Konrad Rudolph