Ist 1

Lesezeit: 1 Minute

Benutzer-Avatar
Ilja Lesochin

Nach der Antwort auf diese Fragen:

Das Ergebnis von E1 << E2 ist E1 links verschoben E2 Bitpositionen; frei gewordene Bits werden mit Nullen aufgefüllt. Wenn E1 einen vorzeichenlosen Typ hat, ist der Wert des Ergebnisses E1 × 2E2, reduziert Modulo eins mehr als der im Ergebnistyp darstellbare Maximalwert. Wenn E1 hat einen vorzeichenbehafteten Typ und einen nichtnegativen Wert und E1 × 2E2 im Ergebnistyp darstellbar ist, dann ist das der Ergebniswert; andernfalls ist das Verhalten undefiniert.

Was darauf hinzudeuten scheint 1 << 31 ist nicht definiert.

GCC gibt jedoch keine Warnung aus, wenn ich es verwende 1 << 31. Es gibt eine für aus 1 << 32.
Verknüpfung

Also, was ist es? Verstehe ich die Norm falsch? Hat GCC eine eigene Interpretation?

  • Mögliches Duplikat von Bedeutung von ((float) rand() / (float)((1 << 31) - 1))

    – Brian Kain

    23. Juli 2017 um 18:24 Uhr

  • @BrianCain nein, das sollte eher als Duplikat von geschlossen werden Dies

    – Antti Haapala – Слава Україні

    23. Juli 2017 um 20:01 Uhr

  • Hier gibt es zwei getrennte Fragen: (a) ist 1 << 31 undefined (was bereits von vielen bestehenden Fragen abgedeckt wird) und (b) warum gibt gcc keine Warnung aus; das ist keine Sprachanwaltsfrage. Ich würde vorschlagen, die Frage so zu bearbeiten, dass sie eindeutig eine dieser beiden ist, aber nicht beide

    – MM

    23. Juli 2017 um 21:20 Uhr


  • Es liegt in Ihrer Verantwortung, auf UB zu achten, nicht auf die des Compilers. Ein Compiler kann Ihnen mit einer Warnung helfen oder auch nicht.

    – n. 1.8e9-wo-ist-meine-Aktie m.

    24. Juli 2017 um 13:50 Uhr

  • Für C++ siehe diesen Kommentar hier für einige interessante Details

    – Shafik Yaghmour

    27. Juli 2017 um 6:31 Uhr

Benutzer-Avatar
chqrlie

Nein: 1 << 31 hat undefiniertes Verhalten, wenn der Typ int hat nur 31 Wertbits.

1U << 31 ist OK und wertet zu 0x80000000 wenn typ unsigned int hat 32 Wertbits.

Auf einem System, wo Bytes 8 Bit haben, sizeof(int) == 4 meint int hat höchstens 31 Wertbits, also ist das Verschieben um 1 um 31 Stellen undefiniert. Umgekehrt auf einem System, wo CHAR_BIT > 8es kann in Ordnung sein, zu schreiben 1 << 31.

gcc kann eine Warnung ausgeben, wenn Sie die Warnstufe erhöhen. Versuchen gcc -Wall -Wextra -W -Werror. clang gibt eine Warnung mit den gleichen Optionen aus.

Um auf die Kommentare von Michaël Roy einzugehen, 1 << 31 tut nicht auswerten zu INT_MIN zuverlässig. Es könnte diesen Wert auf Ihrem System geben, aber der Standard garantiert es nicht, tatsächlich beschreibt der Standard dies als undefiniertes Verhalten, also können Sie sich nicht nur nicht darauf verlassen, Sie sollten es auch vermeiden, um falsche Fehler zu vermeiden. Die Optimierer nutzen routinemäßig potenziell undefiniertes Verhalten, um Code zu entfernen und die Annahmen der Programmierer zu brechen.

Der folgende Code könnte beispielsweise zu einem einfachen kompiliert werden return 1;:

int check_shift(int i) {
   if ((1 << i) > 0)
       return 1;
   else
       return 0;
}

Keiner der Compiler wird von unterstützt Der Compiler-Explorer von Godbolt tun, aber dies würde die Konformität nicht verletzen.

  • @MichaëlRoy – Es ist per Definition undefiniert.

    – Oliver Charlesworth

    23. Juli 2017 um 18:33 Uhr

  • @MichaëlRoy: Es könnte diesen Wert auf Ihrem System geben, aber der Standard garantiert es nicht, tatsächlich beschreibt der Standard dies als undefiniertes Verhalten, also können Sie sich nicht nur nicht darauf verlassen, Sie sollten es vermeiden, um falsche Fehler zu vermeiden. Die Optimierer nutzen routinemäßig potenziell undefiniertes Verhalten, um Code zu entfernen und die Annahmen der Programmierer zu brechen.

    – chqrlie

    23. Juli 2017 um 18:38 Uhr

  • @MichaëlRoy: Auch hier sprechen wir nicht über Asm/Hardware. Wir sprechen über die C-Sprache. C-Compiler/Optimierer arbeiten im letzteren Bereich. Wenn Sie eine genau definierte Art und Weise erhalten möchten 0x80000000 in C dann solltest du das machen 1U << 31.

    – Oliver Charlesworth

    23. Juli 2017 um 18:42 Uhr


  • @MichaëlRoy Sie verwechseln Bitverschiebung und Multiplikation. Nein, ist er nicht. Er ist es tatsächlich zitieren 6.5.7 Bitweise VerschiebungsoperatorenAbsatz 4 des C-Standard. Beachten Sie den Titel von Abschnitt 6.5.7.

    – Andreas Henle

    23. Juli 2017 um 18:59 Uhr

  • @MichaëlRoy: Ich fürchte, die Sprache des C-Standards ist klar. Es ist so formuliert, weil nicht alle Computer Intel-PCs sind. Wenn auf einer anderen CPU das Verschieben eines vorzeichenbehafteten Werts möglicherweise nicht in das Vorzeichenbit überläuft oder eine Ausnahme auslöst, verwendet die CPU möglicherweise nicht die Zweierkomplementdarstellung … das Verhalten von 1 << 31 würde auf diesen CPUs ein anderes Ergebnis liefern. Dies ist ein typischer Grund für die Norm, einen Grenzfall als zu bezeichnen undefiniertes Verhalten. Die Konsequenzen, wenn man sich auf ein Verhalten verlässt, das vom C-Standard nicht gerechtfertigt ist, können schwer zu findende Fehler sein.

    – chqrlie

    23. Juli 2017 um 19:02 Uhr


Der Grund, warum GCC nicht davor warnt, ist, weil 1 << 31 war gültig (aber implementierungsdefiniert) in C90 und ist gültig (aber implementierungsdefiniert) sogar in modernem C++. C90 definiert << als Bitverschiebung und gefolgt von der Aussage, dass das Ergebnis bei vorzeichenlosen Typen das einer Multiplikation war, bei vorzeichenbehafteten Typen jedoch nicht so war, was es implizit gültig machte und es durch den allgemeinen Wortlaut bedeckt ließ, den bitweise Operatoren implementierungsdefiniert haben Aspekte für vorzeichenbehaftete Typen. C++ definiert heutzutage << als Multiplikation mit dem entsprechenden vorzeichenlosen Typ, wobei das Ergebnis wieder in den vorzeichenbehafteten Typ konvertiert wird, der ebenfalls implementierungsdefiniert ist.

C99 und C11 haben dies ungültig gemacht (das Verhalten ist undefiniert), aber Compiler dürfen es als Erweiterung akzeptieren. Aus Gründen der Kompatibilität mit vorhandenem Code und um Code zwischen den C- und C++-Frontends gemeinsam zu nutzen, tut GCC dies weiterhin, mit einer Ausnahme: Sie können verwenden -fsanitize=undefined um erkanntes undefiniertes Verhalten zu erhalten, um Ihr Programm zur Laufzeit abzubrechen, und dieses handhabt es 1 << 31aber nur beim Kompilieren als C99 oder C11.

  • Interessant. Das muss also bedeuten, dass C++ jetzt Out-of-Range-Konvertierungen von unsigniert nach signiert definiert, die zuvor undefiniert gewesen wären. das wusste ich nicht 🙂

    – Oliver Charlesworth

    23. Juli 2017 um 20:28 Uhr

  • @OliverCharlesworth Soweit ich weiß, waren Konvertierungen außerhalb des gültigen Bereichs in vorzeichenbehaftete Integer-Typen von einem anderen Integer-Typ, ob mit oder ohne Vorzeichen, ob mit der gleichen Breite oder größer, in allen Versionen von C und C ++ immer gültig, aber implementierungsdefiniert . Einige Versionen erlauben nur einen implementierungsdefinierten Wert, andere erlauben auch das Auslösen eines implementierungsdefinierten Signals, aber es ist niemals undefiniert.

    Benutzer743382

    23. Juli 2017 um 20:37 Uhr

  • Ich würde sagen, dass es in C90 für diesen Fall unterspezifiziert oder defekt war. Im Gegensatz zu beispielsweise C++14, das speziell den Fall abdeckt, dass es implementierungsdefiniert ist

    – MM

    23. Juli 2017 um 21:16 Uhr


  • @MM C90 scheint es in der Beschreibung für unterspezifiziert zu lassen <<, enthält aber eine allgemeine Klausel, die besagt, dass die binären Operatoren implementierungsdefinierte Aspekte für signierte Typen haben (direkt unter “Ausdrücke”). Das gilt auch hier, deshalb ist es nicht einfach durch Weglassen undefiniert. Ich werde meine Antwort bearbeiten, um dies abzudecken.

    Benutzer743382

    23. Juli 2017 um 21:27 Uhr

  • @BenVoigt Es wird als Fehler in C ++ 11, CWG # 1457 angesehen, daher wird die Änderung rückwirkend angewendet. Was C++03 betrifft, so verwendet dies meistens den C90-Wortlaut für die linke Verschiebungsoperation, aber ich finde nicht den passenden Wortlaut, um zu sagen, dass die bitweisen Operatoren implementierungsdefinierte Aspekte für vorzeichenbehaftete Typen haben. Wenn diese Formulierung nicht vorhanden ist, denke ich, dass Sie dort einen guten Punkt haben.

    Benutzer743382

    24. Juli 2017 um 5:21 Uhr

Es ruft undefiniertes Verhalten auf, wie in den anderen Antworten/Kommentaren erläutert. Warum GCC jedoch keine Diagnose ausgibt.

Es gibt tatsächlich zwei Dinge, die zu einem undefinierten Verhalten für eine Linksverschiebung führen können (beide von [6.5.7]):

  1. Wenn der Wert des rechten Operanden negativ oder größer oder gleich der Breite des heraufgestuften linken Operanden ist, ist das Verhalten nicht definiert.

  2. Wenn E1 einen vorzeichenbehafteten Typ und einen nichtnegativen Wert hat und E1 × 2E2 im Ergebnistyp darstellbar ist, dann ist das der Ergebniswert; andernfalls ist das Verhalten undefiniert.

Offensichtlich erkennt GCC den ersten (weil es trivial ist), aber nicht den zweiten.

  • Eine andere Erklärung könnte sein, dass sich gcc in dieser Situation “vorhersehbar” verhält und die gcc-Entwickler die Benutzer nicht mit Warnungen für Code nerven wollen, der auf gcc (für das ausgewählte Ziel) nicht beschädigt ist. Es liegt nicht in ihrer Verantwortung, vor Code zu warnen, der auf anderen Plattformen oder Compilern beschädigt werden könnte

    – MM

    23. Juli 2017 um 21:34 Uhr

  • Die Autoren des C++-Standards haben sich aus welchen Gründen auch immer für eine Definition entschieden 1<<(bitsize-1) als Ergebnis von ~INT_MAX auf Zweierkomplementmaschinen, obwohl es einen Überlauf beinhaltet, aber entschieden, selbst auf solchen Maschinen keine Linksverschiebungen negativer Werte zu definieren, selbst in Fällen, die keinen Überlauf beinhalten.

    – Superkatze

    25. Oktober 2017 um 19:55 Uhr

1351850cookie-checkIst 1

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy