Ist “-1>>5;” unspezifisches Verhalten in C?

Lesezeit: 9 Minuten

Benutzeravatar von msc
msc

C11 §6.5.7 Absatz 5:

Das Ergebnis von E1 >> E2 ist E1 rechts verschoben E2 Bit-Positionen. Wenn
E1 hat einen vorzeichenlosen Typ oder wenn E1 einen vorzeichenbehafteten Typ und einen nichtnegativen Wert hat, ist der Wert des Ergebnisses der ganzzahlige Teil des Quotienten von E1 / 2*^E2. Wenn E1 einen vorzeichenbehafteten Typ und einen negativen Wert hat, ist der resultierende Wert implementierungsdefiniert.

Aber die viva64 Referenzdokument sagt:

int B;
B = -1 >> 5; // unspecified behavior

Ich habe diesen Code ausgeführt GCC und es gibt immer eine Ausgabe -1.

Also, Standard sagen ist das “Wenn E1 einen vorzeichenbehafteten Typ und einen negativen Wert hat, ist der resultierende Wert implementierungsdefiniert”Aber das Dokument sagen, ist das -1>>5; ist unspezifisches Verhalten.

Also, ist -1>>5; unspezifisches Verhalten in C? Welches ist richtig?

  • Wenn Sie versuchen, portablen Code zu schreiben, ist die Unterscheidung zwischen implementierungsdefiniertem und nicht spezifiziertem Code nicht sehr wichtig, sodass Codeprüfer sie ähnlich behandeln.

    – Barmar

    16. Oktober 2017 um 19:00 Uhr


  • Es ist implementierungsdefiniert.

    – chux – Wiedereinsetzung von Monica

    16. Oktober 2017 um 19:01 Uhr

  • Dieses Dokument zitiert den Standard in seiner Erläuterung.

    – Barmar

    16. Oktober 2017 um 19:03 Uhr

  • Das ist kein Dup. Der gegebene Dup fragt, warum die Rechtsverschiebung von vorzeichenbehafteten Zahlen so funktioniert, wie es funktioniert. Diese Frage fragt nach dem Unterschied zwischen unspezifiziert und Implementierung definiert. Wiedereröffnung.

    – dbusch

    16. Oktober 2017 um 19:06 Uhr

  • Warum wird ein Kopfgeld hinzugefügt? Die Frage hat bereits viele Aufrufe und hoch bewertete Antworten

    – MM

    23. August 2018 um 21:18 Uhr


Benutzeravatar von dbush
dbusch

Beide sind richtig. Implementierungsdefiniertes Verhalten ist eine bestimmte Art von nicht spezifiziertem Verhalten.

Unter Berufung auf Abschnitt 3.4.1 des die C-Norm was “implementierungsdefiniertes Verhalten” definiert:

1 implementierungsdefiniertes Verhalten

unspezifiziertes Verhalten, bei dem jede Implementierung dokumentiert, wie die Auswahl getroffen wird

2 BEISPIEL Ein Beispiel für implementierungsdefiniertes Verhalten ist die Weitergabe des höherwertigen Bits, wenn eine vorzeichenbehaftete Ganzzahl nach rechts verschoben wird.

Aus Abschnitt 3.4.4 Definition von „nicht spezifiziertem Verhalten“:

1 unspezifisches Verhalten

Verwendung eines nicht spezifizierten Wertes oder andere Verhaltensweisen, bei denen diese Internationale Norm zwei oder mehr Möglichkeiten bietet und keine weiteren Anforderungen stellt, die in jedem Fall gewählt wird

2 BEISPIEL Ein Beispiel für nicht spezifiziertes Verhalten ist die Reihenfolge, in der die Argumente einer Funktion ausgewertet werden.

Bei GCC erhalten Sie immer dieselbe Antwort, da die Operation implementierungsdefiniert ist. Es implementiert die Rechtsverschiebung negativer Zahlen über die Vorzeichenerweiterung

Von dem GCC-Dokumentation:

Die Ergebnisse einiger bitweiser Operationen mit vorzeichenbehafteten Ganzzahlen (C90 6.3, C99 und C11 6.5).

Bitweise Operatoren wirken auf die Darstellung des Werts, einschließlich der Vorzeichen- und Wertbits, wobei das Vorzeichenbit unmittelbar über dem höchstwertigen Wertbit betrachtet wird. Unterzeichnet >> wirkt auf negative Zahlen durch Vorzeichenerweiterung.

Als Erweiterung der C-Sprache verwendet GCC den in C99 und C11 angegebenen Spielraum nicht nur, um bestimmte Aspekte von signiert zu behandeln << als undefiniert. Jedoch, -fsanitize=shift (und -fsanitize=undefined) wird solche Fälle diagnostizieren. Sie werden auch dort diagnostiziert, wo konstante Ausdrücke erforderlich sind.

  • Das ist nicht ganz richtig: “Implementierungsdefiniertes Verhalten ist eine besondere Art von nicht spezifiziertem Verhalten”. Wenn ein Verhalten implementierungsdefiniert ist, legt der Standard fest, dass die Implementierung es definieren und dokumentieren muss, sodass es nicht unspezifiziert ist. Unspezifiziertes Verhalten ist für Situationen, in denen die Implementierung frei wählen kann, aber ein Verhalten nicht dokumentieren oder konsistent machen muss.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    16. Oktober 2017 um 22:24 Uhr

  • @R .. Außer dass die Definition von “implementierungsdefiniertem Verhalten” tatsächlich die genauen Wörter “nicht spezifiziertes Verhalten” verwendet. Ich denke, das Erfordernis einer Dokumentation wird nicht als “weitere Anforderungen an die gewählte” angesehen.

    – Aschepler

    16. Oktober 2017 um 23:19 Uhr

  • Ich würde die von Ihnen zitierte Definition von “implementierungsdefiniertem Verhalten” so interpretieren, dass sie “nicht spezifiziertes Verhalten” ausschließt, dh “wie für nicht spezifiziertes Verhalten, aber auch mit dieser Anforderung”. Schließlich beinhaltet die Definition von “nicht spezifiziertem Verhalten” “stellt keine weiteren Anforderungen an die Auswahl”, jedoch stellt implementierungsdefiniertes Verhalten eine Anforderung, dass die Implementierung die Wahl dokumentiert, daher erfüllt es nicht die Definition von “nicht spezifiziertem Verhalten”.

    – MM

    19. Oktober 2017 um 4:15 Uhr


  • @MM-implementierungsspezifisches Verhalten sagt: “dokumentiert, wie die Auswahl getroffen wird” – eine Implementierung kann es also als “zufällig” dokumentieren und dem Standard entsprechen, und es ist nicht von anderem undefinierten Verhalten zu unterscheiden.

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

    27. August 2018 um 2:50 Uhr

  • Ich biete weitere Beweise, ein DR zu C89 🙂

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

    27. August 2018 um 4:44 Uhr

„Nicht spezifiziertes Verhalten“ und „Implementierung definiert“ sind kein Widerspruch. Es bedeutet lediglich, dass der C-Standard nicht vorschreibt, was passieren muss, und dass verschiedene Implementierungen das tun können, was sie für „richtig“ halten.

Das mehrfache Ausführen auf einem Compiler und das Erhalten des gleichen Ergebnisses bedeutet nur das dieser bestimmte Compiler ist konsistent. Auf einem anderen Compiler erhalten Sie möglicherweise andere Ergebnisse.

  • Keiner der Begriffe ist eine Teilmenge des anderen. Wenn eine Aktion „unspezifiziertes“ Verhalten aufruft, müssen Implementierungen aus einer endlichen Menge von Auswahlmöglichkeiten wählen (z x()+y() muss sich so verhalten, als würde es entweder vollständig auswerten x() und wertet dann aus y()oder vollständig y() und dann x(); das sind die einzigen beiden Möglichkeiten). Wenn eine Aktion „implementierungsdefiniertes“ Verhalten aufruft, müssen Implementierungen ein bestimmtes Verhalten dokumentieren, können aber so ziemlich alles tun, was sie wollen, solange sie es dokumentieren.

    – Superkatze

    16. Oktober 2017 um 21:04 Uhr

  • @supercat da steht “document wie die Wahl getroffen wirdnicht dokumentieren die Verhalten gewählt wurde”.

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

    27. August 2018 um 3:40 Uhr


  • @AnttiHaapala: Ich glaube nicht, dass “Implementation-Defined” als Einladung für Implementierungen gedacht ist, einfach zu sagen: “Diese Implementierung wählt unvorhersehbar zwischen diesen Verhaltensweisen aus”. Der Standard stellt keine strengen Anforderungen an die Qualität der Dokumentation von Implementierungen, aber ich denke, die klare Implikation wäre, dass nur Implementierungen von geringer Qualität nichts Nützliches aussagen würden.

    – Superkatze

    27. August 2018 um 13:48 Uhr

  • @supercat, ich sehe nicht, was in der Definition von “nicht spezifiziertem Verhalten” erfordert, dass das Verhalten aus a ausgewählt wird endlich Auswahlmöglichkeiten, es sei denn, Computer können nur eine endliche Anzahl unterschiedlicher Zustände darstellen. „Zwei oder mehr“ vermittelt mir keine Endlichkeit.

    – Johannes Bollinger

    27. August 2018 um 17:55 Uhr


  • @JohnBollinger: Die Stellen im Standard, an denen mir der verwendete Begriff aufgefallen ist, beschreiben Dinge, die eine endliche Anzahl von Auswahlmöglichkeiten hätten, wie z Mitglieder geschrieben. Es wäre für Standard möglich und angemessen gewesen, den Begriff in Fällen zu verwenden, in denen es eine unendliche Anzahl möglicher Verhaltensweisen geben könnte, die alle ziemlich ähnlich sind (z sich darauf beschränken, einen Wert zu liefern …

    – Superkatze

    27. August 2018 um 18:32 Uhr

Implementierungsdefiniertes Verhalten ist a Unterklasse nicht spezifiziertes Verhalten, dh Verhalten, das nicht durch die Norm spezifiziert ist.

Fehlerbericht Nr. 154 an C89 fragte das Komitee, wo die Grenzen liegen implementierungsdefiniertes Verhalten; Das Komitee antwortet, dass eine Implementierung jedes gewünschte Verhalten definieren kann und dass dies nicht konstant sein muss.

Was eine Implementierung tun muss, ist zu dokumentieren wie Diese Wahl wird getroffen, im Gegensatz zu der anderen Klasse des unspezifizierten Verhaltens, bei der eine konforme Implementierung nicht einmal die Mühe machen muss, dies mitzuteilen wie die Wahl wird getroffen, möglicherweise weil für diese die Mehrheit der Implementierungen der Text “zufällig” oder “abhängig von der Compiler-Optimierungsstufe” oder “abhängig von der Registerzuordnung für lokale Variablen” sagen würde.

  • Der Standard vermeidet die Verwendung des Begriffs „implementierungsdefiniertes Verhalten“ in Fällen, in denen von einer erheblichen Anzahl hochwertiger Implementierungen erwartet worden wäre, dass sie „zufällig“ sagen würden. Die Schwelle zwischen UB und IDB scheint im Allgemeinen darin zu liegen, ob die Autoren des Standards sich irgendwelche plausiblen Situationen vorgestellt haben, in denen eine Implementierung möglicherweise nichts Nützliches angeben kann. Betrachten Sie das Verhalten von -1<<x. Es wurde unter C89 definiert, wurde aber unter C99 zu UB, obwohl sich fast alle Implementierungen gleich verhielten. Wenn IDB eine Einladung wäre, “zufällig” zu sagen, dann wären fast alle Formen …

    – Superkatze

    27. August 2018 um 16:28 Uhr

  • …von UB könnte genauso gut IDB sein, aber es besteht die Möglichkeit, dass es eine Implementierung gibt, für die es schwierig sein könnte, ein konsistentes Verhalten zu dokumentieren -1<<x scheint eine angemessene Rechtfertigung dafür gewesen zu sein, dass eine solche Aktion UB aufruft, anstatt sie als IDB zu behandeln [the C99 rationale doesn’t even mention the change, much less give a reason for it].

    – Superkatze

    27. August 2018 um 16:30 Uhr


Ich bekomme keine der vorliegenden Antworten. Der C-Standard sagt eindeutig, dass eine Rechtsverschiebung eine negative Zahl ist implementierungsdefiniertes Verhalten. es ist nicht unspezifisches Verhalten, was etwas anderes bedeutet. Wie Sie richtig zitieren (C17 6.5.7 §5):

Das Ergebnis von E1 >> E2 sind E1-E2-Bitpositionen nach rechts verschoben. /–/
Wenn E1 einen vorzeichenbehafteten Typ und einen negativen Wert hat, ist der resultierende Wert implementierungsdefiniert.

Das bedeutet, dass der Compiler muss dokumentieren, wie es sich verhält. Zeitraum.

In der Praxis: Das Dokument muss angeben, ob der Compiler arithmetische Rechtsverschiebung oder logische Rechtsverschiebung verwendet.


Dies steht im Gegensatz zu nicht spezifiziertem Verhalten, das ein implementierungsspezifisches Verhalten ist nicht müssen dokumentiert werden. Nicht spezifiziertes Verhalten wird in zwei Fällen verwendet:

  • Wenn das Compiler-Verhalten ein Implementierungsgeheimnis sein könnte, das der Compiler-Anbieter nicht gezwungen sein sollte, es seinen Konkurrenten preiszugeben.
  • Wenn der Compiler sich nicht die Mühe machen kann, zu dokumentieren, wie die zugrunde liegenden Details wie Betriebssystem und RAM-Speicherzellen funktionieren.

Beispielsweise muss ein Compiler die Reihenfolge der Auswertung nicht in folgendem Code dokumentieren:

a  = f1() + f2();
a += f1() + f2();

Die Dokumentation der Reihenfolge, in der die Unterausdrücke ausgewertet werden, würde Details darüber enthüllen, wie der interne Ausdrucksbaum und der Optimierer des Compilers funktionieren, was wiederum zeigen würde, warum ein Compiler besseren Code produziert oder schneller kompiliert als die Konkurrenz. Das war eine große Sache, als der C-Standard ursprünglich geschrieben wurde. Heutzutage weniger, wenn es einige großartige Open-Source-Compiler gibt, sodass es kein Geheimnis mehr ist.

Ebenso muss ein Compiler nicht dokumentieren, was dieser Code ausgibt:

int a;
int ptr = &a;
printf("%d", *ptr);

a ist ein unbestimmter Wert und die Ausgabe ist unspezifiziert – in der Praxis hängt die Ausgabe davon ab, was zuvor in dieser bestimmten RAM-Zelle gespeichert wurde. Was wir einen “Müllwert” nennen würden. (Bevor Sie “UB” schreien, lesen Sie (Warum) verwendet eine nicht initialisierte Variable undefiniertes Verhalten?).

1395250cookie-checkIst “-1>>5;” unspezifisches Verhalten in C?

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

Privacy policy