Schlüsselwort ‘restrict’ – Warum ist es erlaubt, von einer äußeren eingeschränkten Variablen zu einer inneren eingeschränkten Variablen zuzuweisen?

Lesezeit: 7 Minuten

Benutzer-Avatar
Norswap

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 die register 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 bereitstellt P als einschränkungsqualifizierter Zeiger auf den Typ T.

Wenn D erscheint innerhalb eines Blocks und hat keine Speicherklasse externLassen B bezeichnen den Block. Wenn D erscheint in der Liste der Parameterdeklarationen einer Funktionsdefinition, let B bezeichnet den zugehörigen Block. Ansonsten lassen B 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 Objekt P if (an irgendeinem Sequenzpunkt in der Ausführung von B vor der Auswertung von E) modifizieren P auf eine Kopie des Array-Objekts zu zeigen, auf das es zuvor zeigte, würde den Wert von ändern E.119) Beachten Sie, dass „based“ nur für Ausdrücke mit Zeigertypen definiert ist.

Bei jeder Ausführung von BLassen L ein beliebiger lvalue sein, der hat &L bezogen auf
P. Wenn L wird verwendet, um auf den Wert des Objekts zuzugreifen X dass es bezeichnet, und X 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 wird X hat auch seine Adresse basierend auf P. Jeder Zugriff, der sich ändert X gelten auch als zu ändern P, für die Zwecke dieses Unterabschnitts. Wenn P wird der Wert eines Zeigerausdrucks zugewiesen E das auf einem anderen eingeschränkten Zeigerobjekt basiert P2verbunden mit block B2dann entweder die Ausführung von B2
beginnt vor der Vollstreckung Boder die Ausführung von B2 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 dies restrict ermöglicht es einem Compiler, sich so zu verhalten, als ob jeder Lesevorgang durch a restrict 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) und void bar(int *restrict c, int *restrict d)vermutlich stimmen Sie dem zu bar sollte anrufen können foo(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

Benutzer-Avatar
Superkatze

Ich denke, die Regeln sollen zwei Ziele erreichen:

  1. Ermöglicht die Erstellung eines temporären Zeigers, ähnlich dem, der natürlich erstellt würde, wenn ein Argument an einen Funktionsaufruf übergeben wird, ohne dass Code, der den Zeiger verwendet, in eine physisch separate Funktion verschoben werden muss.

  2. Stellen Sie sicher, dass ein Diagramm, das die Zeigerableitung zeigt, frei von Zyklen ist (was bedeutet, dass, wenn der Zeiger x von y oder irgendetwas abgeleitet ist, das direkt oder indirekt von y abgeleitet ist, y nicht von x abgeleitet werden kann oder irgendetwas, das direkt oder indirekt von x abgeleitet wird). . Während die Regeln strenger sein können, als es absolut notwendig wäre, um Nr. 2 zu erreichen, erfüllen fast alle nützlichen Fälle, die diese zweite Anforderung erfüllen würden, auch die Regeln in der geschriebenen Form, und Compiler hätten Schwierigkeiten, daraus einen großen Nutzen zu ziehen restrict in den Fällen, in denen dies nicht der Fall ist.

Leider scheinen sich die Autoren der Regeln nicht besonders bemüht zu haben, alle möglichen Eckfälle zu berücksichtigen und sicherzustellen, dass die Regeln des Standards auf alle sinnvoll angewendet werden können. Es gibt zwei Anwendungsfälle, in denen die Semantik von restrict sind klar und sinnvoll:

  1. Eine Funktion erhält a restrict-Qualifizierter Zeiger, in diesem Fall sollte sich der Qualifizierer auswirken der an die Funktion übergebene Zeigerwertunabhängig von allem anderen, was im Argument gespeichert sein könnte.

  2. Die Definition eines automatischen Objekts mit einem einschränkenden Qualifizierer enthält einen Initialisierungsausdruck, in welchem ​​Fall der Qualifizierer wirken sollte der für die Initialisierung verwendete Zeigerwertunabhängig von allem anderen, was im Objekt gespeichert sein könnte.

Die Semantik, irgendetwas anderes als eine Adresse, die über eines der beiden oben genannten Mittel in einem Zeiger gespeichert ist, einzuschränken, “zu bewachen”, ist bestenfalls ziemlich düster, selbst wenn man versucht, Geltungsbereichsregeln wie die im Standard hinzuzufügen. Das Hinzufügen von einschränkenden Qualifizierern in anderen Fällen würde eher etwas zerstören, als dass es einen nützlichen Effekt hätte, obwohl das wahrscheinlichste Ergebnis wäre, dass die Qualifizierer überhaupt keine Wirkung haben würden.

Keines davon ist ein undefiniertes Verhalten. Sie können zwischen Beschränkungszeigern alles zuweisen, was Sie wollen. Undefiniertes Verhalten kann auftreten, wenn Sie den Objekten, auf die Beschränkungszeiger verweisen, falsche Zuweisungen zuweisen.

Und in Ihrem zweiten Beispiel wird der Zeiger y von x abgeleitet, also ist es auch in Ordnung, zuerst *x und dann dieselbe Variable wie *y zuzuweisen.

Anstatt Juristensprache zu lesen, haben Sie sich eigentlich überlegt, was „einschränken“ bewirken soll?

  • Nun, der Standard widerspricht Ihnen ohne Zweifel. Und ja, ich habe darüber nachgedacht; und ich stelle die Frage, da mein intuitives Verständnis genau nicht mit dem übereinstimmt, was der Standard sagt. Ich gehe davon aus, dass der Standard nicht von Vollidioten geschrieben wurde, also muss es einen Grund für seine Macken geben. Auch dies ist ein Kommentar; keine Antwort.

    – Norswap

    26. März 2015 um 14:49 Uhr


  • Könnten Sie darauf hinweisen, wo ich mich irre, anstatt ad hominems auszuteilen? Bedenken Sie auch, dass es nicht nur meine Lektüre ist, sondern auch die anderer Stackoverflow-Benutzer zu der Frage, die ich verlinkt habe und hierohne dass jemand widersprechen könnte (das beweist natürlich nichts).

    – Norswap

    26. März 2015 um 16:45 Uhr

  • @Norswap: Viele Eckfälle sind skurril, weil die Autoren des Standards keine effektiven Anstrengungen unternommen haben, um sie vernünftig zu handhaben – vermutlich, weil sie sie nicht berücksichtigt haben. Da der Standard zulässt, dass sich Implementierungen in Fällen, in denen dies nicht erforderlich ist, vernünftig verhalten, legten die Autoren eine höhere Priorität darauf, zu vermeiden, dass Compiler mit Eckfällen schlecht umgehen müssen, als dass sie von ihnen verlangen, dass sie Eckfälle gut handhaben.

    – Superkatze

    8. Februar um 16:03 Uhr

1033180cookie-checkSchlüsselwort ‘restrict’ – Warum ist es erlaubt, von einer äußeren eingeschränkten Variablen zu einer inneren eingeschränkten Variablen zuzuweisen?

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

Privacy policy