2016-07-13 9 views
15

Fragen:Verstößt dieser Code gegen die strenge Aliasing-Regel?

  1. Ist dieser Code verletzen unter strengen Aliasing-Regeln? Das heißt, würde ein intelligenter Compiler 00000 (oder einen anderen unangenehmen Effekt) drucken können, weil dann auf einen Puffer, auf den zuerst als anderer Typ zugegriffen wurde, über int* zugegriffen wird?

  2. Wenn nicht, würde nur die Definition bewegen und initializaton von ptr2 vor den Klammern (so würde ptr2 bereits definiert werden, wenn ptr1 zu Umfang kommt) brechen?

  3. Wenn nicht, würde das Entfernen der Klammern (also ptr1 und ptr2 waren im gleichen Umfang) brechen Sie es?

  4. Wenn ja, wie könnte der Code behoben werden?

Bonus Frage: Ist der Code in Ordnung ist, und 2. oder 3. Sie es auch nicht brechen, wie es zu ändern, damit es strenge Aliasing Regeln brechen würde (zB umwandeln verspannten Schleife int16_t zu verwenden) ?


int i; 
void *buf = calloc(5, sizeof(int)); // buf initialized to 0 

{ 
    char *ptr1 = buf;  
    for(i = 0; i < 5*sizeof(int); ++i) 
     ptr1[i] = i; 
} 

int *ptr2 = buf; 
for(i = 0; i < 5; ++i) 
    printf("%d", ptr2[i]); 

zur Bestätigung der Suche, so kurz (ish), kompetente Antwort zu diesem speziellen Code, idealerweise mit minimalem Standard zitiert, ist das, was ich bin nach. Ich bin nicht nach langen Erklärungen der strengen Aliasregeln, nur die Teile, die zu diesem Code gehören. Und es wäre großartig, wenn eine Antwort die oben genannten nummerierten Fragen explizit aufzählen würde.

Nehmen Sie auch eine Allzweck-CPU ohne Ganzzahl-Trap-Werte an, und sagen wir auch int ist 32 Bit und Zweierkomplement.

Antwort

13

Nein, aber das liegt nur daran, dass der Speicher zugewiesen und mit einem Zeichentyp beschrieben wurde.

Speicher wird mit malloc zugewiesen. Dieses Objekt hat Typ nicht deklariert, da es mit malloc zugewiesen wurde. Somit hat das Objekt keinen effektiven Typ.

Anschließend greift der Code auf das Objekt zu und ändert es mit dem Typ char. Da der Typ char und kein Objekt eine effektive Art, die kopiert , Kopieren nicht die effektivste Art zu char für diese und nachfolgende Zugriffe festgelegt, sondern setzt die effektive Art zu char, nur für die Dauer der der Zugang . Nach dem Zugriff hat das Objekt keinen gültigen Typ mehr.

Dann wird der Typ int verwendet, um auf dieses Objekt zuzugreifen und dieses nur zu lesen. Da das Objekt keinen effektiven Typ hat, wird es für die Dauer des Lesens int. Nach dem Zugriff hat das Objekt keinen gültigen Typ mehr. Da int offensichtlich mit dem effektiven Typ int kompatibel war, ist das Verhalten definiert.

(vorausgesetzt, die Werte gelesen werden, nicht abfangen Darstellung für int.)


Haben Sie zugegriffen und modifizierten, um die Aufgabe, einen nicht-Zeichentyp verwendet, die ebenfalls mit int nicht kompatibel ist, wäre das Verhalten nicht definiert sein, .

Angenommen, Ihr Beispiel wurde (sizeof(float)==sizeof(int) vorausgesetzt):

int i; 
void *buf = calloc(5, sizeof(float)); // buf initialized to 0 

{ 
    float *ptr1 = buf;  
    for(i = 0; i < 5*sizeof(float); ++i) 
     ptr1[i] = (float)i; 
} 

int *ptr2 = buf; 
for(i = 0; i < 5; ++i) 
    printf("%d", ptr2[i]); 

Die effektive Art des Objekts, wenn float s in geschrieben werden, wird vom Typ float, für die Dauer des Schreib- und alle nachfolgenden Zugriffe auf das Objekt, die es nicht ändern . Wenn diese Objekte dann zugegriffen werden, indem der effektive int Typ float bleibt, da die Werte nicht nur modifiziert gelesen werden. Der vorherige Schreibvorgang mit float setzt den effektiven Typ dauerhaft auf float bis zum nächsten Schreibvorgang in dieses Objekt (was in diesem Fall nicht der Fall war). Die Typen int und float sind nicht kompatibel , daher ist das Verhalten nicht definiert.


(: ISO: Alle nachstehenden Text wird aus zitiert IEC 9899: 201x)

(6,5 Expressions 6)
Die effektive Typ eines Objekts für einen Zugriff auf seine gespeicherte Wert der deklarierte Typ des Objekts, falls vorhanden. 87) Zugewiesene Objekte haben keinen deklarierten Typ.

(6,5 Expressions 6)
Wenn ein Wert in ein Objekt gespeichert wird, einen Typ mit nicht deklarierten Typ durch einen L-Wert aufweist, der keine Zeichentyp ist, dann wird der Typ der L-Wert der effektiven Typ des Objekts für diesen Zugriff und für nachfolgende Zugriffe, die den gespeicherten Wert nicht ändern.

(6,5 Expressions 6)
für alle anderen zu einem Objekt zugreift keine deklarierten Typ aufweist, ist der effektive Art des Objekts einfach der Typ der L-Wert für den Zugriff verwendet.

(6,5 Expressions 8)
Ein Objekt hat seinen gespeicherten Wert nur durch einen L-Wert Ausdruck zugegriffen hat, die die folgenden Arten ein von hat: 88) - ein Typen kompatibel mit dem wirksamen Typ des Objekts, , - eine qualifizierte Version eines Typs, der mit dem effektiven Objekttyp kompatibel ist, - ein Typ, der dem Typ des Typs mit Vorzeichen oder Vorzeichen entspricht, - ein Typ mit oder ohne Vorzeichen entsprechend einer qualifizierten Version des effektiven Typs des Objekts, ein Aggregat oder Union Typ, der (ein Mitglied einer Unteraggregat oder enthaltenen Union einschließlich, rekursiv) oder einen des oben genannten Typs unter seinen Mitgliedern umfasst - einen Zeichentyp.

(6.5 Expressions 6)
Wenn ein Wert in ein Objekt kopiert wird unter Verwendung von Memcpy oder memmove nicht deklarierten Typ aufweist, oder wird als ein Array von Zeichentyps kopiert, dann wird der effektive Art des modifizierten Objekts für diesen Zugriff und nachfolgende Zugriffe die tun Ändern Sie den Wert nicht ist der effektive Typ des Objekts, von dem der Wert kopiert wird, wenn es einen Wert hat.

+0

Sie einen interessanten Punkt implizit erhöhen, dass ich nicht gedacht hatte: da die zugewiesenen Speicher über eine 'int' lvalue nicht zugeordnet ist, hat es noch nicht„werden“ein' int' pro 6.5 Absatz 6, wenn es dereferenziert über ein 'int *', also doch striktes Aliasing? Das Parsen dieses Absatzes ist schmerzhaft. –

+1

An diesem Punkt wurde nur ein Zeichen in das Objekt geschrieben und der effektive Typ wurde nicht auf das Objekt gesetzt. Bei allen anderen Zugriffen auf ein Objekt, das keinen deklarierten Typ hat, wird der tatsächliche Typ des Objekts einfach der Typ des für den Zugriff verwendeten lvalue. * – 2501

+1

Wenn an demselben Punkt 'int 'wurde in das Objekt geschrieben, statt zu lesen, der Typ würde auch 'int' werden, weil: * Wenn ein Wert in einem Objekt gespeichert wird, das keinen deklarierten Typ durch einen lvalue mit einem Typ hat, der kein Zeichentyp ist, dann type des lvalue wird der effektive Typ des Objekts für diesen Zugriff und für nachfolgende Zugriffe, die den gespeicherten Wert nicht verändern. * – 2501

2

No. Dies verstößt nicht gegen strenges Aliasing.

Von the C Standard, 6.2.5 Typen Absatz 28:

Ein Zeiger auf void ist die gleiche Darstellung und Ausrichtungsanforderungen als Zeiger auf einen Zeichentyp hat. 48

Notiere die 48. Diese 48 Fußnote bezieht:

48) die gleiche Darstellung und Ausrichtungsanforderungen sollen Austauschbarkeit als Argumente an Funktionen implizieren, Rückgabewerte von Funktionen, und Mitglieder von Gewerkschaften.

So können Sie die calloc() ‚d-Speicher über einen char * Zeiger zugreifen (vorausgesetzt, dass Ihr ptr soll ptr1 sein) ohne Probleme.

Obwohl das wirklich besonders ist, da 7.22.3 Speicherverwaltungsfunktionen Absatz 1 heißt es:

Der Zeiger zurückgegeben, wenn die Zuordnung gelingt es in geeigneter Weise so ausgerichtet ist , dass es zu einem Zeiger zugewiesen werden können, um jede Art von Objekt mit einer grundlegende Anforderung Ausrichtung und dann verwendet, um Sie für den Zugriff auf ein solches Objekt oder ein Array solcher Objekte im Raum

zugeordnet

so kann auch über einen int Zeiger, sowie einen char Zeiger auf den calloc() Speicher zugreifen. Und ein double Zeiger zum Booten (vorausgesetzt, Sie bleiben innerhalb der Grenzen des zugewiesenen Speichers).

+6

Strict Aliasing ist etwas anderes als Ausrichtung Anforderungen und Darstellung. –

+1

@ JohannesSchaub-litb Was die Frage bezieht, ist die gleiche Sache 'memset()' tut (* Die 'memset' Funktion kopiert den Wert von ' C' (umgerechnet auf eine 'unsigned char' ) in jede der ersten 'n' Zeichen des Objekts, auf das von ' s' . *) Wenn Sie denken, dass es falsch ist, posten Sie Ihre Antwort, die auch erklärt, wie 'memset()' kann jede Art von Erinnerung an einen wiederholten 'char'-Wert. Willst du sagen, dass 'memset()' das strikte Aliasing verletzt? –

+3

@AndrewHenle niemand sagt, dass der Code in der Frage strenge Aliasing-Regeln verletzt. Die Gründe dafür, warum dies der Fall ist, sind jedoch nicht korrekt. Bei gleicher Ausrichtung und Darstellung bedeutet _nicht_, dass zwei Typen einen Aliasnamen haben können. – davmac

Verwandte Themen