Ist die Verwendung von reinterpret_cast auf einem memcpy-Puffer UB?

8

Angesichts des Codes

struct A {};

auto obj = new A;
std::vector<unsigned char> buffer;
buffer.resize(sizeof(obj));
std::memcpy(buffer.data(), &obj, sizeof(obj));  // this copies the pointer, not the object!

// ...

auto ptr = *reinterpret_cast<A**>(buffer.data()); // is this UB?
delete ptr;

ist die Verwendung von reinterpret_castin diesem Fall UB? Ich würde ja sagen, da memcpydie Lebensdauer einer Instanz nicht beginnt und somit gegen die strenge Aliasing-Regel verstößt (weshalb std::bit_castC ++ 20 hinzugefügt wurde).

Und wenn ich die Besetzung durch eine andere ersetze memcpy(um den Zeiger zu lesen), wäre das Programm gut definiert?

Timo
quelle
Abgesehen von der Anwaltschaft in der Sprache ist es einfach falsch. Es ist der Inhalt, auf den gezeigt wird buffer.data(), der angeblich einen Zeiger auf enthält A, nicht buffer.data()selbst, der ein Zeiger auf ist A.
StoryTeller - Unslander Monica
3
Gibt es Ausrichtungsgarantien für den durch a zugewiesenen Speicher des Sicherungsspeichers std::vector? (Ich
nehme
1
Ich denke, es bricht auch striktes Aliasing.
Ein Programmierer
1
@anastaciu Erster Treffer bei Google - stats.meta.stackexchange.com/q/5783/3512
Konrad Rudolph
1
OK, nachdem die Frage erneut gelesen wurde, handelt es sich tatsächlich um UB, da (1) die Ausrichtungsanforderungen möglicherweise fehlerhaft sind und (2) sich hier kein A*Objekt im Puffer befindet. Der Standard sagt über die Standardfunktion allocator :: allocate aus: "Rückgabe: Ein Zeiger auf das Anfangselement eines Arrays von Speichergrößen n * sizeof(T), das für Objekte vom Typ T entsprechend ausgerichtet ist ".
n. 'Pronomen' m.

Antworten:

9

Ja, dieser Code hat ein undefiniertes Verhalten. A*An der Stelle, auf die von gezeigt wird, befindet sich kein Objekt vom Typ buffer.data(). Sie haben lediglich die Objektdarstellung eines solchen Zeigers in Ihren Vektor [basic.types] / 4 kopiert . Da Zeiger trivial kopierbar sind [basic.types] / 9 , wenn Sie diese Bytes in ein tatsächliches Objekt vom Typ A*und dann deleteden Wert davon zurückkopieren würden, wäre dies gut definiert [basic.types] / 3 . Also das

A* ptr;
std::memcpy(&ptr, buffer.data(), sizeof(ptr));
delete ptr;

wäre in Ordnung.

Beachten Sie, dass nicht die Umwandlung selbst in Ihrem ursprünglichen Beispiel undefiniertes Verhalten hervorruft, sondern Ihr nachfolgender Versuch, den Wert eines Objekts vom Typ zu lesen, das A*nicht vorhanden ist, wenn der Zeiger über die Umwandlungspunkte erhalten wurde. Alles, was dort ist, wo der Zeiger zeigt, ist eine Folge von Objekten vom Typ unsigned char. Der Typ A*ist kein Typ, mit dem Sie auf den gespeicherten Wert eines Objekts vom Typ unsigned char [basic.lval] / 8zugreifen können.

Michael Kenzel
quelle
2
Ich glaube, diese Antwort ist richtig, aber der letzte Satz ist ein bisschen überraschend, da es eine gute Menge an vorhandenem Code gibt, der im Wesentlichen das tut, was die Frage verlangt (zunächst praktisch jede benutzerdefinierte Container-Implementierung), und es gibt derzeit keine wirklich gute in einigen Fällen umgehen.
Konrad Rudolph
1
A* a_ptr; std::memcpy(&a_ptr, buffer.data(), sizeof(a_ptr));um den Zeiger wieder aus dem Puffer zu extrahieren. Anstatt der reinterpret_castin Timos Frage.
Eljay
Abgesehen von der Sprachanwaltschaft, warum ist das eigentlich UB? Strict Aliasing kommt mir in den Sinn, gibt es noch etwas anderes?
Divinas
@ MichaelKenzel dort gibt es ein Objekt vom Typ unsigned char, vielleicht mehr als eines.
n. 'Pronomen' m.
1
@ MichaelKenzel Natürlich ist es nicht zu verteidigen, da es UB ist. Das Initiieren der Objektlebensdauer ist in (aktuellem) C ++ jedoch sehr schwierig, und Tonnen von (ansonsten qualitativ hochwertigem!) Code funktionieren nicht ordnungsgemäß, siehe insbesondere p0593r2 §2.3 , aber auch §2.2. Und da Sie die Komprimierung erwähnen: Genau das macht mein Unternehmen, und sagen wir einfach, dass unsere Codebasis diese Probleme nicht im geringsten berücksichtigt (zugegebenermaßen, da ein Großteil davon von idiomatischem C abgeleitet ist).
Konrad Rudolph