2016-12-27 3 views
3

Mir scheinen ein paar Puzzleteile in Bezug auf effektiven Typ wieder zu fehlen ... Die Kommentare im Code sind im Wesentlichen meine Frage, aber es ist der einzige Weg, wie ich diese Frage richtig stellen kann Kontext.Verwirrt durch effektive Typregeln

Bin ich richtig in der Annahme, dass alles in Ordnung ist, oder irre ich mich in einigen Fällen? Ich stelle fest, dass dies grenzwertig mehrere Fragen stellt, aber ich frage im Wesentlichen, ob mein Verständnis von effektivem Typ und strengem Aliasing richtig ist. GCC produziert keine Diagnose für mich mit -pedantic-errors -Wextra -Wall -O2 -fstrict-aliasing -Wstrict-aliasing

+2

'buf = (void *) f' ist immer gültig (unabhängig vom Typ von' f'), weil die strengen Aliasregeln eine Ausnahme haben, die es einem 'char *' erlaubt, einen anderen Typ zu aliasieren. – user3386109

Antwort

4

Sie scheinen verwechselt zu werden, was "effektiver Typ" betrifft: Es gilt für den Malloc'd-Raum, nicht für jeden Zeiger. Wie immer in C ist ein Zeiger ein separates Objekt mit separaten Eigenschaften für jeden Raum, auf den der Zeiger möglicherweise zeigt.

f ist eine (benannte) Variable, daher ist ihr effektiver Typ immer derselbe wie der deklarierte Typ, nämlich Foo *. Ähnlich ist der effektive Typ buf immer char *. Der einzige Zeitpunkt, zu dem sich der effektive Typ zur Laufzeit ändern kann, ist der dynamisch zugewiesene Speicherplatz.

Ihre Aufzählungspunkte und die Code-Kommentare sind wenig sinnvoll, daher habe ich beschlossen, Ihren Code neu zu kommentieren. Der Text bezieht sich jeweils auf den Code über dem Text:

Foo *f = malloc(sizeof(Foo)); 

OK. Nicht initialisierte Bytes wurden zugewiesen und f Punkte zu ihnen. Der dynamisch zugewiesene Speicherplatz hat noch keinen effektiven Typ.

f->n = 1; 

Die effektive Art des ersten sizeof(int) Bytes wird dynamisch zugewiesenen Speicherplatz zu int eingestellt. (* - aber siehe Fußnote)

char *buf = malloc(sizeof(Foo)); 
memcpy(buf, f, sizeof(Foo)); 

Die memcpy Funktion bewahrt effektive Art von Objekten kopiert, so dass die effektive Art des ersten sizeof(int) Bytes des Raumes, auf den buf ist int.

((Foo *)buf)->n++; 

Erstens der Guss nicht über ein Ausrichtungsproblem, weil malloc Raum korrekt für jede Art ausgerichtet ist. Wenn Sie auf den Zugriff von n gehen, ist dies in Ordnung, weil ((Foo *)buf)->n ein Wert vom Typ int ist und ein Objekt vom effektiven Typ int bezeichnet. So können wir problemlos lesen und schreiben.

memcpy(f, buf, sizeof(Foo)); 

memcpy ist immer in Ordnung, da es die effektive Art setzt (Ihr Kommentar vorgeschlagen, dass Memcpy in einigen Fällen nicht in Ordnung sein könnte). Diese Zeile legt den effektiven Typ des Speicherplatzes fest, auf den f zeigt, bis int (da der Quellbereich effektiven Typ hatte int).

f->n++; 

Fein, gleiches Grundprinzip wie ((Foo *)buf)->n++ oben.

free(buf); 
buf = (void *)f; 

Redundante Besetzung. Der Raum, auf den f zeigt, hat den effektiven Typ int immer noch, da keine dieser Zeilen in diesen Raum geschrieben hat.

free(f); 

Kein Problem.


Fußnote: Manche Menschen nehmen eine andere Interpretation des Ausdrucks f->n (oder ((Foo *)buf)->n WRT strenge Aliasing Sie sagen, dass f->n als (*f).n definiert ist und daher der damit verbundene effektive Art ist die Art der *f, nicht die Art von. f->n Ich stimme dieser Ansicht nicht zu, daher werde ich nicht weiter darauf eingehen.Es gab Vorschläge für C2X, um diese Situation und andere Grauzonen des strengen Aliasing zu klären.Für Ihren speziellen Code ist der Code immer noch korrekt Interpretation obwohl.

+0

Ich füge einfach hinzu, dass der Rückgabewert von malloc verifiziert werden sollte. Und das 'Foo * f = malloc (sizeof * f);' ist eine gute Übung. – Stargateur

+0

@Stargateur Einverstanden. Der Kürze halber habe ich die Überprüfung meiner Frage weggelassen und die Typnamen verwendet, um hervorzuheben, dass es sich um Zuweisungen, Lesevorgänge und Schreibvorgänge derselben Größe handelt. Entschuldigung dafür, das nicht zu erwähnen. :-) –

+0

"Sie sagen, dass' f-> n' definiert ist als '(* f) .n' ..." Sie sind falsch. Es ist C++, das '->' durch eine äquivalente Kombination von '*' und '.' definiert, aber nicht C. In der C-Sprachspezifikation ist der Operator' -> 'unabhängig definiert. – AnT

0

Der Code gültig ist und — für mit polymorphen Daten umzugehen Objekte — war, wie es vor dem C++ fertig war.

Es gibt jedoch nicht viel mehr, das von diesen Operationen extrapoliert werden kann, bevor man sich in den Fuß schießen könnte. Das könnte von Foo und sagen Sie einen Typ Foo2, die eine andere Größe ist und dann Zugriff auf Elemente in einem, die nicht da sind, weil die zugehörige malloc() war nicht groß genug.

Im Allgemeinen ist es verständlicher und wahrscheinlich richtig, wenn der Zeigertyp immer der gleiche wie der malloc() ist. Für einen schickeren Morphismus ist C++ wahrscheinlich weniger fehleranfällig (solange seine Warnungen nicht unterdrückt werden).

1
// No effective type yet because there has been no write to the memory. 
Foo *f = malloc(sizeof(Foo)); 

Hier gibt es keine Zugriffe und keine Objekte. Der effektive Typ ist nicht relevant.

// Effective type of `f` is now `Foo *`, except I'm writing to 
// `f->n`, so shouldn't it be `int *`? Not sure what's going on here. 
f->n = 1; 

Objekt an der ersten sizeof (Foo) Bytes an der Adresse f, hat eine effektive Art Foo, und das Objekt an der ersten sizeof (int) Bytes an der Adresse f eine effektive Art int hat.

// No effective type yet because there has been no write to the memory. 
char *buf = malloc(sizeof(Foo)); 

Es gibt keine Zugriffe hier und keine Objekte. Der effektive Typ ist nicht relevant.

// Effective type of `buf` is `Foo *`, despite it being declared as 
// `char *`. 
// It's not safe to modify `buf` as a `char` array since the effective type 
// is not `char`, or am I missing something? 
memcpy(buf, f, sizeof(Foo)); 

Objekt an der ersten sizeof (Foo) Bytes an der Adresse, sondern hat eine effektive Art Foo, und das Objekt an der ersten sizeof (int) Bytes an der Adresse, sondern hat eine effektive Art Int.

Auf jedes Objekt kann unabhängig von seinem effektiven Typ mit einem Zeichentyp zugegriffen werden. Sie können auf Bytes von buf mit char zugreifen.

// The cast here is well defined because effective type of `buf` is 
// `Foo *` anyway, right? 
((Foo *)buf)->n++; 

Ja. Der gesamte Ausdruck ist gültig.

// I'm not even sure this is OK. The effective type of `buf` is `Foo *`, 
// right? Why wouldn't it be OK then? 
memcpy(f, buf, sizeof(Foo)); 

Das ist in Ordnung. memcpy ändert den Objekttyp an der Adresse f in den Typ Foo. Auch wenn f vorher noch keinen Typ Foo hatte, tut es das jetzt.

// Safe if the last `memcpy` was safe. 
f->n++; 

Ja.

// buf now points to invalid memory. 
free(buf); 

Ja.

// Pointers with different declared types point to the same object, 
// but they have the same effective type that is not qualified by 
// `restrict`, so this doesn't violate strict aliasing, right? 
// This is allowed because `f` was allocated via `malloc`, 
// meaning it is suitably aligned for any data type, so 
// the effective type rules aren't violated either. 
buf = (void *)f; 

Sie mischen Konzepte. Einschränkungen und Werte einzelner Zeiger sind für das Aliasing nicht relevant. Der Zugang ist. Pointer buf zeigt nun einfach auf Adresse f.

// `f`, and consequently `buf` since it points to the same object as `f`, 
// now point to invalid memory. 
free(f); 

Ja.