C Speicherzuordner und striktes Aliasing

8

selbst nachdem ich ziemlich viel über die strengen Aliasing-Regeln gelesen habe, bin ich immer noch verwirrt. Soweit ich das verstanden habe, ist es unmöglich, einen vernünftigen Speicherzuordner zu implementieren, der diese Regeln befolgt, da Malloc niemals freigegebenen Speicher wiederverwenden kann, da der Speicher verwendet werden könnte, um verschiedene Typen bei jeder Zuweisung zu speichern.

Das kann eindeutig nicht stimmen. Was vermisse ich? Wie implementieren Sie einen Zuordner (oder einen Speicherpool), der strengen Aliasing folgt?

Danke.

Bearbeiten: Lassen Sie mich meine Frage mit einem dummen einfachen Beispiel klären:

%Vor%     
Sebastian Ende 07.10.2011, 12:15
quelle

4 Antworten

8

Ich glaube nicht, dass Sie recht haben. Selbst die strengsten strengen Aliasregeln würden nur zählen, wenn der Speicher tatsächlich für einen bestimmten Zweck zugewiesen wird. Sobald ein zugewiesener Block mit free auf den Heap freigegeben wurde, sollte es keine Referenzen mehr geben und er kann wieder von malloc ausgegeben werden.

Und das von void* zurückgegebene malloc unterliegt nicht der strengen Aliasing-Regel, da der Standard ausdrücklich angibt, dass ein void-Zeiger in jede andere Art von Zeiger (und wieder zurück) umgewandelt werden kann. C99 Abschnitt 7.20.3 besagt:

  

Der Zeiger, der zurückgegeben wird, wenn die Zuweisung erfolgreich ist, wird passend ausgerichtet, so dass er einem Zeiger auf einen beliebigen Objekttyp zugewiesen werden kann und dann auf ein solches Objekt oder ein Array solcher Objekte in dem zugewiesenen Raum zugreifen kann (bis der Raum ist) explizit freigegeben).

In Bezug auf Ihr Update (das Beispiel), wo Sie nicht tatsächlich den Speicher zurück zum Heap zurückgeben, denke ich, dass Ihre Verwirrung entsteht, weil zugeordnete Objekte speziell behandelt werden. Wenn Sie auf 6.5/6 von C99 verweisen, sehen Sie:

  

Der effektive Typ eines Objekts für einen Zugriff auf seinen gespeicherten Wert ist der deklarierte Typ des Objekts, falls vorhanden (Fußnote 75: Zugewiesene Objekte haben keinen deklarierten Typ).

Lies diese Fußnote noch einmal, es ist wichtig.

  

Wenn ein Wert in einem Objekt ohne deklarierten Typ über einen L-Wert mit einem Typ gespeichert wird, der kein Zeichentyp ist, dann wird der Typ des L-Wertes zum effektiven Typ des Objekts für diesen Zugriff und für nachfolgende Zugriffe, die dies tun Ändern Sie den gespeicherten Wert nicht.

     

Wenn ein Wert mit memcpy oder memmove in ein Objekt ohne deklarierten Typ kopiert wird oder als ein Array von Zeichentypen kopiert wird, dann wird der effektive Typ des modifizierten Objekts für diesen Zugriff und für nachfolgende Zugriffe, die den value ist der effektive Typ des Objekts, von dem der Wert kopiert wird, falls er einen hat.

     

Bei allen anderen Zugriffen auf ein Objekt ohne deklarierten Typ ist der effektive Typ des Objekts einfach der Typ des für den Zugriff verwendeten lvalue.

Mit anderen Worten, der zugewiesene Blockinhalt wird zum Typ des Datenelements, das Sie dort abgelegt haben.

Wenn Sie float eingeben, sollten Sie nur als float (oder kompatiblen Typ) darauf zugreifen. Wenn Sie ein int einfügen, sollten Sie es nur als int (oder kompatiblen Typ) verarbeiten.

Die einzige Sache, die Sie nicht tun sollten ist, einen bestimmten Typ von Variablen in diesen Speicher zu legen und dann zu versuchen, sie als einen anderen Typ zu behandeln - ein Grund dafür ist, dass Objekte erlaubt sind haben Trap-Repräsentationen (die zu undefiniertem Verhalten führen) und diese Repräsentationen können auftreten, weil das gleiche Objekt als verschiedene Typen behandelt wird.

Wenn Sie also ein int vor der Freigabe in Ihrem Code speichern und dann als float -Zeiger neu zuweisen, sollten Sie nicht versuchen, das Float bis zu Ihnen zu verwenden Ich habe tatsächlich einen dort hineingestellt. Bis zu diesem Zeitpunkt ist der Typ des Zugewiesenen noch nicht float .

    
paxdiablo 07.10.2011, 12:19
quelle
1

Ich poste diese Antwort, um mein Verständnis von strengem Aliasing zu testen:

Striktes Aliasing ist nur dann von Bedeutung, wenn tatsächlich Lese- und Schreibvorgänge stattfinden. Genauso wie die Verwendung mehrerer Elemente unterschiedlichen Typs gleichzeitig ein undefiniertes Verhalten ist, gilt das Gleiche auch für Zeiger: Sie können keine Zeiger unterschiedlichen Typs verwenden, um aus demselben Grund auf denselben Speicher zuzugreifen, wie Sie es bei einer Vereinigung nicht tun können.

Wenn Sie nur einen der Zeiger als live betrachten, dann ist das kein Problem.

  • Wenn Sie also über int* schreiben und int* durchlesen, ist es in Ordnung.
  • Wenn Sie mit int* schreiben und float* lesen, ist es schlecht.
  • Wenn Sie mit int* schreiben und später wieder mit float* schreiben, dann lesen Sie es mit float* , dann ist es in Ordnung.

Bei nicht-trivialen Allokatoren haben Sie einen großen Puffer, den Sie normalerweise in einem char* speichern. Dann machen Sie eine Art Zeigerarithmetik, um die Adresse zu berechnen, die Sie zuweisen möchten, und dann denentieren Sie sie über die Kopfzeilenstrukturen des Zuordners. Es spielt keine Rolle, welche Zeiger Sie verwenden, um die Zeigerarithmetik durchzuführen, nur den Zeiger, den Sie den Bereich durch Angelegenheiten dereferenzieren. Da Sie dies in einem Allokator immer über die Kopfzeilenstruktur des Zuordners tun, werden Sie dadurch kein undefiniertes Verhalten auslösen.

    
Calmarius 28.11.2015 17:52
quelle
0

Beziehen Sie sich innerhalb des Allokators selbst nur auf Ihre Speicherpuffer als (void *). Wenn es optimiert wird, sollten die Optimierungen für das strikte Aliasing nicht vom Compiler angewendet werden (weil dieses Modul keine Ahnung hat, welche Typen dort gespeichert sind). Wenn dieses Objekt mit dem Rest des Systems verbunden wird, sollte es alleine gelassen werden.

Hoffe, das hilft!

    
Woodrow Douglass 07.10.2011 12:22
quelle
0

Standard C definiert kein effizientes Mittel, mit dem ein benutzergeschriebener Speicherzuordner einen Speicherbereich, der als ein Typ verwendet wurde, sicher annehmen und ihn sicher als einen anderen verfügbar machen kann. Strukturen in C sind garantiert keine Repräsentationen einzufangen - eine Garantie, die wenig Sinn machen würde, wenn es nicht möglich wäre, Strukturen mit Feldern zu kopieren, die Indeterminate Value enthalten.

Die Schwierigkeit besteht darin, dass man eine Struktur und Funktion wie folgt erhält:

%Vor%

Es sollte möglich sein, es wie folgt aufzurufen:

%Vor%

, ohne zuerst alle Felder der zugeordneten Struktur schreiben zu müssen. Obwohl malloc garantiert, dass der Zuweisungsblock zurückgegeben werden kann Wenn sie von einem beliebigen Typ verwendet werden, gibt es keine Möglichkeit für eine vom Benutzer beschriebene Speicherverwaltung Funktionen, um eine solche Wiederverwendung von Speicher zu ermöglichen, ohne sie entweder zu löschen byteweise (mit einer Schleife oder einem memset) oder auch mit free () und malloc () um den Speicher zu recyceln.

    
supercat 08.01.2017 22:59
quelle