2015-02-22 3 views
11

C11 6.7.3 Typ-Qualifikation Absatz 7 lautet:Anforderungen an das Verhalten der Zeiger auf flüchtige zeigt auf nicht-flüchtiges Objekt

Ein Objekt, das flüchtige qualifizierte Typ hat, kann unbekannt in einer Weise modifiziert werden, zur Umsetzung oder haben andere unbekannte Nebenwirkungen. Daher ist jeder Ausdruck, der sich auf einen solchen Gegenstand bezieht, streng nach den Regeln der abstrakten Maschine zu bewerten, wie in 5.1.2.3 beschrieben.

Im folgenden Beispiel wird auf das Objekt in der dritten Zeile zugegriffen, die der obigen Regel unterliegt?

int x; 
volatile int *p = &x; 
*p = 42; 

Mit anderen Worten, die Tatsache, dass der L-Wert *pvolatile int bedeutet, ist Typ, der ein flüchtiges Objekt zugegriffen wird, oder hat die Tatsache, dass p Punkte auf ein nichtflüchtigen Objekt x bedeuten, dass die Compiler optimieren mit diesem Wissen und lassen Sie den flüchtigen Zugang weg?

Da es von Interesse sein kann, ist der spezielle Anwendungsfall, den ich interessiere, außerhalb des Bereichs von Ebene C; es beinhaltet atomics für Thread-Synchronisation vorge C11-Konstrukte unter Verwendung von (die Inline-asm oder einfach als Blackbox gedacht sein könnte) für atomaren Vergleichs- und Swap mit der folgenden Idiom:

do { 
    tmp = *p; 
    new = f(tmp); 
} while (atomic_cas(p, tmp, new) != success); 

Hier wird der Zeiger p hätte Typ volatile int *, aber ich bin besorgt darüber, was passiert, wenn die tatsächlich zackigen auf nicht-flüchtiges Objekt, insbesondere ob der Compiler den einzigen Zugang zu *p von tmp = *p in zwei Zugriffen auf die folgende Form umwandeln könnte:

do { 
    new = f(*p); 
} while (atomic_cas(p, *p, new) != success); 

was offensichtlich wäre ly render den Code falsch. Das Ziel besteht also darin, zu bestimmen, ob alle solchen angezeigten Objekte tatsächlich volatile int sein müssen.

+2

Ein konformes Programm kann den Unterschied nicht erkennen, da der Zugriff auf und die Änderung von 'x' keine beobachtbaren Nebenwirkungen sind. Daher kann der Compiler unter "Als-ob-Regel" optimieren. –

+1

@IgorTandetnik: Das ist die Art und Weise, wie ich es interpretiere, aber es scheint keine populäre Theorie zu sein, und ich frage immer noch, ob es richtig ist. Wenn auf x nicht zugegriffen wird, außer als "* (volatile int *) & x", und "& x" wird für Hardware sichtbar gemacht, die darauf zugreift, oder wenn "sig_atomic_t" ist "int" und "* (volatile int *) & x Wird von einem Signal-Handler zugegriffen, gibt es dann möglicherweise wahrnehmbare beobachtbare Nebenwirkungen? –

+2

Wenn die Hardware auf x zugreift und Sie sie als nichtflüchtig deklarieren, implementiert die Hardwarekonfiguration die virtuelle C-Maschine nicht. – philipxy

Antwort

7

aktualisieren 18. Februar 2017

Die Antwort unten zitiert & diskutiert die Sprache in der Norm, einige widersprüchliche Sprache in der Begründung und einige Kommentare von gnu.cc Wieder den Widerspruch. Es gibt einen Fehlerbericht, der im Wesentlichen eine Committee-Vereinbarung enthält (obwohl noch offen), dass der Standard sagen sollte, und dass die Absicht immer gewesen ist, und dass Implementierungen immer reflektiert haben, dass es nicht die Volatilität von eines Objekts ist (nach dem Standard) aber der Volatilität von (der Wert von) ein Zugriff (per der Rationale). (Credit Olaf für diesen DR zu erwähnen.)

Defect Report Summary für C11 Version 1.10 Datum: April 2016 DR 476 volatile semantics for lvalues 04/2016 Offene


Nr Da das zugegriffen Objekt nicht flüchtig ist.

Objekt p ist vom Typ Zeiger auf flüchtige int. Aber x ist kein Objekt eines Typs mit flüchtiger Qualifizierung. Die Qualifikationen auf p beeinflussen, welche Zugriffe durch sie ausgeführt werden können, beeinflussen jedoch nicht den Typ des Objekts, auf das es verweist. Es gibt keine Einschränkung für den Zugriff auf ein nicht-qualifiziertes Objekt über einen flüchtigen lvalue.Der Zugriff auf x durch p ist also kein Zugriff auf ein Objekt mit einem flüchtigen Typ.

(siehe 6.7.3 Typ Qualifikation für die Einschränkungen für Objekte von qualifizierten Typen zugreifen. Er sagt, Sie nicht nur ein flüchtiges qualifiziertes Objekt über einen uneingeschränkten lvalue zugreifen können.)

Auf der anderen Seite, this post Zitate aus dem 6.7.3 der Rationale für den Internationalen Standard - Programmiersprachen - C:

Eine Umwandlung eines Wertes in einen qualifizierten Typ hat keine Auswirkung; Die Qualifizierung (flüchtig, sagen wir) kann keine Auswirkung auf den Zugriff haben, da es vor dem Fall aufgetreten ist. Wenn auf ein nichtflüchtiges Objekt mit flüchtiger Semantik zugegriffen werden muss, besteht die Technik darin, die Adresse des Objekts in den entsprechenden Zeiger für den qualifizierten Typ zu konvertieren und dann den Zeiger diesen Wert zu dereferenzieren.

Allerdings kann ich im Standard keine Sprache finden, die besagt, dass die Semantik auf dem Lvalue-Typ basiert. Von gnu.org:

Ein Bereich, der Verwirrung ist die Unterscheidung zwischen Objekten mit flüchtigen Typen definiert und flüchtigen lvalues. Vom Standpunkt des C-Standards mit der Ansicht aus hat ein Objekt, das mit einem flüchtigen Typ definiert ist, äußerlich sichtbares Verhalten . Sie können sich an solche Objekte denken, die kleine Oszilloskop-Sonden haben, so dass der Benutzer einige Eigenschaften von Zugriffen auf sie beobachten kann, genau wie der Benutzer Daten beobachten kann, die in Ausgabedateien geschrieben wurden. Der Standard macht jedoch nicht deutlich, ob Benutzer Zugriffe durch flüchtige Werte auf gewöhnliche Objekte beobachten können.

[..] Aus dem Standard ist nicht klar, ob volatile lvalues ​​ allgemein mehr Garantien bieten als nichtflüchtige lvalues, wenn die zugrunde liegenden Objekte gewöhnlich sind.

Nein, denn es gibt keine Nebenwirkungen:

Auch wenn die Semantik von *p dass ein flüchtigen sein muß, der Standard dennoch sagt:

5.1.2.3 Programmausführung 4 In der abstrakten Maschine werden alle Ausdrücke wie in der Semantik angegeben ausgewertet. Eine tatsächliche Implementierung muss einen Teil eines Ausdrucks nicht auswerten, wenn er daraus ableiten kann, dass sein -Wert nicht verwendet wird und keine erforderlichen Nebenwirkungen erzeugt werden (einschließlich aller durch Aufruf einer Funktion oder Zugriff auf ein flüchtiges Objekt ).

Auch hier gibt es kein flüchtiges Objekt in Ihrem Code. Obwohl eine Kompilierungseinheit, die nur sieht, konnte diese Optimierung nicht vornehmen.

Denken Sie auch daran

6.7.3 Typ-Qualifikation 7 [...] Was einen Zugriff auf ein Objekt mit einem Typ mit flüchtiger Qualifizierung ausmacht, ist implementierungsdefiniert.

5.1.2.3 Programmausführung 8 Strengere Korrespondenzen zwischen abstrakten und tatsächlichen Semantik kann durch jede Implementierung definiert werden.

So bloßen Erscheinungen von flüchtigen lvalues ​​Ihnen nicht sagen, was "Zugänge" gibt es. Sie haben nicht das Recht, über "den einfachen Zugang zu *p von tmp = *p" zu sprechen, mit Ausnahme des dokumentierten Implementierungsverhaltens.

+2

Das Zitat aus dem Rationale scheint eine vom normativen Text abweichende Absicht anzuzeigen ... :-( –

+1

Auch wenn ein Objekt ist "gewöhnlich", würde ich vorschlagen, dass es möglich ist, die Möglichkeit zuzulassen, dass Interrupts, andere Threads usw. möglicherweise mit "flüchtiger" Semantik darauf zugreifen müssen, unter der Bedingung, dass sich nicht flüchtige Zugriffe auf unerwartete Weise verhalten können Es gibt reale Situationen, in denen Code Puffer zur Laufzeit zuordnen muss und auf sie mit flüchtiger Semantik zugegriffen werden muss.Würde ein von "malloc" zurückgegebener Puffer als "flüchtig" betrachtet werden, würden Zugriffe über nichtflüchtige Zeiger UB aufrufen. also ein solcher Puffer muss "gewöhnlich" sein. Aber wenn die Gewöhnlichkeit von ... – supercat

+1

... der Puffer bedeutete, dass Compiler "flüchtige" Qualifizierer auf Zeigern zu seinem Inhalt ignorieren konnten, die Semantik notwendig Daten zwischen Kontexten zu teilen wäre unerreichbar. – supercat

2

Nicht ganz sicher, aber ich denke, der Punkt ist der Unterschied zwischen der Art ein Objekt hat und die Art ein Objekt mit definiert ist.

Von C11 (n1570) 6.3.2.1 p1 (Fußnote weggelassen, emph Mine.):

Ein lvalue ist ein Ausdruck (mit einem Objekttyp anders als void), die möglicherweise ein Objekt bezeichnet; Wenn ein Lvalue bei der Auswertung kein Objekt angibt, ist das Verhalten nicht definiert. Wenn ein Objekt einen bestimmten Typ hat, wird der Typ durch den Lvalue angegeben, der zur Bezeichnung des Objekts verwendet wird.

Es ist der Lvalue, der den Typ definiert, den ein Objekt für einen bestimmten Zugriff hat. Im Gegensatz dazu bezeichnet *p kein Objekt definiert flüchtig. Zum Beispiel ebenda. 6.7.3 p6 (emph. Mine) liest

[...] Wenn ein Versuch unternommen wird, auf ein Objekt Bezug zu nehmen definiert mit einer flüchtigen qualifizierten Art durch die Verwendung eines L-Wertes mit nicht-volatile- Qualifizierter Typ, das Verhalten ist nicht definiert. 133)

133) Das auf diese Objekte gilt, die als verhalten, wenn sie mit qualifizierten Typen definiert wurden, auch wenn sie nie als Objekte im Programm (wie zum Beispiel ein Objekt in einem Memory-Mapped definiert tatsächlich Eingabe-/Ausgabeadresse).

Wenn es die Absicht, dem Code zu ermöglichen ist, würde das Zitat in der Frage gelesen gezeigt werden optimierte heraus, wahrscheinlich ein Objekt, die hat flüchtige qualifizierten Typen [mit einem definierten] [modifiziert werden kann. ..]

"Definition" eine Kennung *) wird in 6.7, Absatz 5 definiert

Ebd. 6.7.3 p7 (Was einen Zugriff auf ein Objekt mit flüchtig-qualifiziertem Typ darstellt, ist implementierungsdefiniert.) gibt den Implementierern einen gewissen Spielraum, aber für mich scheint die Absicht der Nebeneffekt zu sein, das Objekt zu modifizieren bezeichnet durch n sollte durch eine konforme Implementierung als beobachtbar angesehen werden.

*) Afaik der Standard definiert nicht "ein Objekt definiert mit (irgendein Typ)" irgendwo, also lese ich es als "ein Objekt, bestimmt durch einen Bezeichner, der mit einer Definition durch irgendeine Definition erklärt wird".

+0

Ich neige dazu, diese Interpretation auf der Grundlage Ihrer Analyse des Wortes "hat" zu akzeptieren. Eine vergleichbare Sprache für "const" erscheint in der Definition von "modifizierbarem lvalue", wo sie "* keinen Const-qualifizierten Typ" hat. Wenn in diesem Fall "have" auf den Typ verweist, mit dem das Objekt in seiner Definition deklariert wurde, und nicht auf den Typ des verwendeten Ausdrucks, wäre '* (const int *) & x' ein modifizierbarer Lvalue, was natürlich nicht der Fall ist die Absicht. Es gibt einen Unterschied zwischen "einem Objekt, das hat" und "einem Ausdruck ..., der möglicherweise ein Objekt bezeichnet, das nicht" –

+0

hat, aber es scheint mir immer noch sinnvoll, dass das "Objekt" in dem Text, den ich ursprünglich zitiert habe, ist das durch den Wert lvalue angegebene Objekt mit dem Typ des Ausdrucks lvalue. –

+0

Ich wäre vorsichtig mit der Analyse der "vergleichbaren Sprache". Der Standard wurde von vielen Leuten über viele Edits produziert und es scheint unwahrscheinlich, dass sie sich bemüht hätten, die Sprache für diese beiden Orte zu synchronisieren –

Verwandte Themen