Verwendet malloc für int undefiniertes Verhalten bis C ++ 20

96

Mir wurde gesagt, dass der folgende Code bis C ++ 20 ein undefiniertes Verhalten aufweist:

int *p = (int*)malloc(sizeof(int));
*p = 10;

Ist das wahr?

Das Argument war, dass die Lebensdauer des intObjekts nicht vor dem Zuweisen des Werts gestartet wird ( P0593R6 ). Um das Problem zu beheben, newsollte die Platzierung verwendet werden:

int *p = (int*)malloc(sizeof(int));
new (p) int;
*p = 10;

Müssen wir wirklich einen Standardkonstruktor aufrufen, der trivial ist, um die Lebensdauer des Objekts zu starten?

Gleichzeitig hat der Code in reinem C kein undefiniertes Verhalten. Aber was ist, wenn ich einen intin C-Code zuordne und ihn in C ++ - Code verwende?

// C source code:
int *alloc_int(void)
{
    int *p = (int*)malloc(sizeof(int));
    *p = 10;
    return p;
}

// C++ source code:
extern "C" int *alloc_int(void);

auto p = alloc_int();
*p = 20;

Ist es noch undefiniertes Verhalten?

anton_rh
quelle
8
Für int? Nein. Für std::string? Ja.
Eljay
8
@ Eljay Für int, auch ja. Es ist nur so, dass es in der Praxis keine Probleme verursacht, wenn Sie es nicht tun. Denn std::stringes wird offensichtlich Probleme verursachen.
Barry
Vor C ++ 20 können Sie eine neue Platzierung hinzufügen. Es wäre dann gut geformt und würde wahrscheinlich nichts kosten.
François Andrieux
8
Welche neuen Regeln in C ++ 20 ändern dies?
Kevin
4
Sollte es nicht sein int *p = (int*)malloc(sizeof(int)); p = new(p) int;? Ich habe einmal festgestellt, dass das Nichtzuweisen des Ergebnisses der Platzierung auch fatale Auswirkungen haben kann (obwohl es ein bisschen albern aussehen könnte).
Scheff

Antworten:

62

Ist es wahr?

Ja. Technisch gesehen kein Teil von:

int *p = (int*)malloc(sizeof(int));

Erstellt tatsächlich ein Objekt vom Typ int, sodass die Dereferenzierung pUB ist, da dort kein Objekt vorhanden ist int.

Müssen wir wirklich einen Standardkonstruktor aufrufen, der trivial ist, um die Lebensdauer des Objekts zu starten?

Haben Sie müssen pro C ++ Objektmodell nicht definiertes Verhalten pre-C ++ 20 zu vermeiden? Ja. Wird ein Compiler tatsächlich Schaden anrichten, wenn Sie dies nicht tun? Nicht dass ich wüsste.

[...] Ist es noch undefiniertes Verhalten?

Ja. Vor C ++ 20 haben Sie noch intnirgendwo ein Objekt erstellt, also ist dies UB.

Barry
quelle
Kommentare sind nicht für eine ausführliche Diskussion gedacht. Dieses Gespräch wurde in den Chat verschoben .
Makyen
Warum reicht die Sprache in timsong-cpp.github.io/cppwp/n3337/basic.life#1.1 nicht aus, um nicht UB zu sein? Schließlich wurde intim Beispiel eine Lagerung mit der richtigen Größe und Ausrichtung erhalten - intdort beginnt die Lebensdauer des Objekts.
Avakar
41

Ja, es war UB. Die Liste der Möglichkeiten, wie eine intDose existieren kann, wurde aufgezählt, und dort gilt keine, es sei denn, Sie halten Malloc für kausal.

Es wurde allgemein als ein Fehler im Standard angesehen, der jedoch von geringer Bedeutung war, da die von C ++ - Compilern für dieses bestimmte UB-Bit vorgenommenen Optimierungen keine Probleme mit diesem Anwendungsfall verursachten.

Was die zweite Frage betrifft, schreibt C ++ nicht vor, wie C ++ und C interagieren. Jede Interaktion mit C ist also ... UB, auch bekannt als Verhalten, das vom C ++ - Standard nicht definiert wird.

Yakk - Adam Nevraumont
quelle
5
Können Sie die aufgezählte Liste der Möglichkeiten für die Existenz eines Int erweitern? Ich erinnere mich, dass ich eine ähnliche Frage über die Lebensdauer primitiver Typen gestellt habe und mir gesagt wurde, dass ein Primitiv "existieren" könnte, indem ich einfach sage, dass es existiert, weil die Spezifikation nichts anderes gesagt hat. Es hört sich so an, als hätte ich einen nützlichen Abschnitt der Spezifikation verpasst! Ich würde gerne wissen, welchen Abschnitt ich hätte lesen sollen!
Cort Ammon
7
@CortAmmon Die aufgezählte Liste der Möglichkeiten, wie ein Objekt (eines beliebigen Typs) in C ++ 20 existieren kann, befindet sich in [intro.object] : (1) per Definition (2) durch neuen Ausdruck (3) implizit gemäß den neuen Regeln in P0593 (4) das aktive Mitglied einer Gewerkschaft (5) vorübergehend ändern. (3) ist neu in C ++ 20, (4) war neu in C ++ 17.
Barry
3
Ist C / C ++ Interaktion wirklich UB? Es wäre sinnvoller, implementierungsdefiniert als undefiniert zu sein, sonst wäre es seltsam, überhaupt die extern "C"Syntax zu haben.
Ruslan
4
@ Ruslan: Implementierungen können jedes Verhalten definieren, das ISO C ++ undefiniert lässt. (Zum Beispiel gcc -fno-strict-aliasingoder standardmäßig MSVC). Wenn Sie "Implementierung definiert" sagen, müssen alle C ++ - Implementierungen eine Art und Weise definieren, in der sie mit einer C-Implementierung zusammenarbeiten. Daher ist es sinnvoll, die Implementierung vollständig zu überlassen, ob sie so etwas tun möchten oder nicht.
Peter Cordes
4
@PeterCordes: Ich frage mich, warum so viele Menschen diese Unterscheidung zwischen IDB und UB nicht erkennen und eine phantasievolle Vorstellung davon vertreten, dass das Versäumnis des Standards, alle Implementierungen zu verarbeiten, ein Konstrukt sinnvoll impliziert, ein Urteil impliziert, dass keine Implementierungen erwartet werden sollten. und Implementierungen, die dies nicht tun, dürfen nicht als minderwertig angesehen werden.
Supercat