Zunächst einige Referenzen. Das C99-Standard sagt dies über restrict
in Abschnitt 6.7.3:
Ein Objekt, auf das über einen eingeschränkt qualifizierten Zeiger zugegriffen wird, hat eine spezielle Zuordnung zu diesem Zeiger. Diese in 6.7.3.1 unten definierte Assoziation erfordert, dass alle Zugriffe auf dieses Objekt direkt oder indirekt den Wert dieses bestimmten Zeigers verwenden.117) Die bestimmungsgemäße Verwendung der
restrict
Qualifizierer (wie dieregister
Speicherklasse) soll die Optimierung fördern, und das Löschen aller Instanzen des Qualifizierers aus allen vorverarbeitenden Übersetzungseinheiten, die ein konformes Programm bilden, ändert nicht seine Bedeutung (dh beobachtbares Verhalten).
Und dann (§6.7.3.1 „Formale Definition von restrict
“):
Lassen
D
eine Deklaration eines gewöhnlichen Bezeichners sein, der ein Mittel zur Bezeichnung eines Objekts bereitstelltP
als einschränkungsqualifizierter Zeiger auf den TypT
.Wenn
D
erscheint innerhalb eines Blocks und hat keine Speicherklasseextern
LassenB
bezeichnen den Block. WennD
erscheint in der Liste der Parameterdeklarationen einer Funktionsdefinition, letB
bezeichnet den zugehörigen Block. Ansonsten lassenB
bezeichnen den Hauptblock (oder den Block einer beliebigen Funktion, die beim Programmstart in einer freistehenden Umgebung aufgerufen wird).Im Folgenden ein Zeigerausdruck
E
wird gesagt, dass basierend auf ObjektP
if (an irgendeinem Sequenzpunkt in der Ausführung vonB
vor der Auswertung vonE
) modifizierenP
auf eine Kopie des Array-Objekts zu zeigen, auf das es zuvor zeigte, würde den Wert von ändernE
.119) Beachten Sie, dass „based“ nur für Ausdrücke mit Zeigertypen definiert ist.Bei jeder Ausführung von
B
LassenL
ein beliebiger lvalue sein, der hat&L
bezogen auf
P
. WennL
wird verwendet, um auf den Wert des Objekts zuzugreifenX
dass es bezeichnet, undX
auch (auf welche Weise auch immer) modifiziert wird, gelten folgende Anforderungen:T
darf nicht const-qualifiziert sein. Jeder andere lvalue, der für den Zugriff auf den Wert von verwendet wirdX
hat auch seine Adresse basierend aufP
. Jeder Zugriff, der sich ändertX
gelten auch als zu ändernP
, für die Zwecke dieses Unterabschnitts. WennP
wird der Wert eines Zeigerausdrucks zugewiesenE
das auf einem anderen eingeschränkten Zeigerobjekt basiertP2
verbunden mit blockB2
dann entweder die Ausführung vonB2
beginnt vor der VollstreckungB
oder die Ausführung vonB2
endet vor der Beauftragung. Wenn diese Anforderungen nicht erfüllt sind, ist das Verhalten undefiniert.
Wie einige darauf hingewiesen haben, veranschaulicht dies die Regeln (Beispiel 4 aus dem Standard):
{
int * restrict p1;
int * restrict q1;
p1 = q1; // undefined behavior
{
int * restrict p2 = p1; // valid
int * restrict q2 = q1; // valid
p1 = q2; // undefined behavior
p2 = q2; // undefined behavior
}
}
Jetzt mein erste Frage ist dies: Warum ist es in Ordnung, von einem äußeren eingeschränkten Zeiger auf einen inneren zuzuweisen?
Mein Verständnis ist, dass nichts dies verbietet, was ein klares Aliasing hat:
int * restricted x = /* ... */ ;
{
int * restricted y = x;
*x = 3;
printf("%d\n", *y); // 3
*y = 4;
printf("%d\n", *x); // 4
}
Natürlich ist der Satz von Aliasnamen auf die zwei Zeiger beschränkt.
Daher meine zweite Frage: Was ist der Unterschied von außen nach innen (erlaubt), aber nicht von innen nach außen (verboten, z p1 = q1;
im ersten Beispiel oben)?
Bitte ändern Sie es so, dass es tatsächlich kompiliert.
– gnasher729
26. März 2015 um 10:58 Uhr
War nicht der einzige Ort, an dem Sie einen Zeiger als deklarieren können
restrict
in der Parameterliste zu einer Funktion?– fuz
26. März 2015 um 10:58 Uhr
@FUZxxl Nein, der Standard sagt dies nicht und es funktioniert einwandfrei, wenn alle Warnungen und die strikte Einhaltung von Standards aktiviert sind.
– Norswap
26. März 2015 um 14:50 Uhr
Die Semantik von
restrict
wäre verständlicher gewesen, wenn es beschrieben hätte, was der Compiler tun durfte, als was dem Programmierer verboten ist, aber ich denke, die Grundidee ist diesrestrict
ermöglicht es einem Compiler, sich so zu verhalten, als ob jeder Lesevorgang durch arestrict
Der Zeiger wird an einer beliebigen Stelle in der Ausführungssequenz zwischen der Zuweisung des Zeigers und dem Punkt ausgeführt, an dem der logische Lesevorgang auftritt, und Schreibvorgänge dürfen sich so verhalten, als ob sie zu einem beliebigen Zeitpunkt innerhalb der Lebensdauer des Zeigers über den Ort hinaus auftreten, an dem der logische Schreibvorgang auftritt, mit Ausnahme von Lesevorgängen und Schreibvorgänge können nicht auftreten …– Superkatze
11. Juli 2016 um 18:50 Uhr
Wenn Sie zwei Funktionen haben
void foo(int *restrict a, int *restrict b)
undvoid bar(int *restrict c, int *restrict d)
vermutlich stimmen Sie dem zubar
sollte anrufen könnenfoo(c, d)
. Sie dürfen auch “inline”foo
“von Hand”, indem die Implementierung in einem verschachtelten Block platziert wird.– Tavian Barnes
19. Januar 2017 um 18:09 Uhr