2788 Stimmen

Muss ich das Ergebnis von malloc casten?

En diese Frage schlug jemand in einer Kommentar dass ich no werfen das Ergebnis von malloc . d.h., ich sollte dies tun:

int *sieve = malloc(sizeof(*sieve) * length);

statt:

int *sieve = (int *) malloc(sizeof(*sieve) * length);

Warum sollte dies der Fall sein?

35 Stimmen

9 Stimmen

Gussformen sind böse. Ich sehe so viele Abdrücke im Code, die nur das Ergebnis einer schlechten Programmierpraxis sind. Wann immer Sie einen Cast einfügen müssen, sollten Sie sich als erstes fragen, was hier falsch ist. Ist alles so deklariert, wie es sein sollte? Wenn ja, wäre kein Cast erforderlich, also ist etwas falsch deklariert. Wenn Sie wirklich etwas auf niedriger Ebene mit einzelnen Bytes in einem int oder so tun müssen, ziehen Sie eine Union in Betracht, um auf sie zuzugreifen. Das wird sie genau richtig deklarieren. Als Faustregel gilt: Fügen Sie sie nicht ein, es sei denn, der Compiler beschwert sich. Dann vermeiden Sie sie. Dieses Beispiel wird sich nicht beschweren. void pointer wird zu jedem Typ promoten.

2 Stimmen

@HansLepoeter in C++ sind diese für malloc erforderlich, was meine Vermutung stützt, dass damit etwas nicht stimmt

32voto

August Karlstrom Punkte 10019

In der Sprache C kann ein ungültiger Zeiger einem beliebigen Zeiger zugewiesen werden, weshalb Sie keinen Typ-Cast verwenden sollten. Wenn Sie eine "typsichere" Zuweisung wünschen, kann ich die folgenden Makrofunktionen empfehlen, die ich in meinen C-Projekten immer verwende:

#include <stdlib.h>
#define NEW_ARRAY(ptr, n) (ptr) = malloc((n) * sizeof *(ptr))
#define NEW(ptr) NEW_ARRAY((ptr), 1)

Wenn diese vorhanden sind, können Sie einfach sagen

NEW_ARRAY(sieve, length);

Für nicht-dynamische Arrays ist das dritte "Must-have"-Funktionsmakro

#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])

was Array-Schleifen sicherer und bequemer macht:

int i, a[100];

for (i = 0; i < LEN(a); i++) {
   ...
}

2 Stimmen

"ein ungültiger Zeiger kann jedem beliebigen Objekt zugewiesen werden Objekt Zeiger" Funktionszeiger sind ein weiteres Problem, wenn auch kein malloc() ein.

1 Stimmen

Zuweisung einer void* zu/von einem Funktionszeiger können Informationen verloren gehen, so dass die Aussage "ein ungültiger Zeiger kann einem beliebigen Zeiger zugewiesen werden" in diesen Fällen ein Problem darstellt. Die Zuweisung eines void* , von malloc() zu jeder Objekt Der Zeiger ist jedoch kein Thema.

26voto

Diese Frage ist Gegenstand von Meinungsmissbrauch.

Manchmal bemerke ich solche Kommentare:

Das Ergebnis von malloc nicht casten

ou

Warum man das Ergebnis von malloc nicht casten sollte

bei Fragen, bei denen der OP das Gießen verwendet. Die Kommentare selbst enthalten einen Hyperlink zu dieser Frage.

Das ist in cualquier möglicherweise unangemessen und auch falsch. Es gibt kein Richtig und kein Falsch, wenn es wirklich um den eigenen Kodierungsstil geht.


Warum ist das so?

Dafür gibt es zwei Gründe:

  1. Diese Frage ist in der Tat meinungsabhängig. Technisch gesehen hätte die Frage schon vor Jahren als meinungsbasiert geschlossen werden müssen. A " Will ich " oder " Nein, ich nicht " oder gleichwertig " Sollte ich " oder " Sollte ich nicht "Frage kann man nicht gezielt beantworten, ohne eine eigene Meinung zu haben. Einer der Gründe, eine Frage zu schließen, ist, dass sie "zu meinungsbasierten Antworten führen könnte", wie es hier gut gezeigt wird.

  2. Viele Antworten (einschließlich der offensichtlichsten und am meisten akzeptierten Antwort de @unwind ) sind entweder vollständig oder fast vollständig meinungsbasiert (z. B. ein mysteriöses "Durcheinander", das Ihrem Code hinzugefügt würde, wenn Sie den Cast durchführen oder sich wiederholen, wäre schlecht) und zeigen eine klare und gezielte Tendenz zum Weglassen des Casts. Sie argumentieren einerseits mit der Redundanz des Casts, andererseits aber auch - und das ist noch schlimmer - mit der Lösung eines Fehlers, der durch einen Fehler in der Programmierung selbst verursacht wurde - also damit, dass man nicht #include <stdlib.h> wenn man Folgendes verwenden möchte malloc() .


Ich möchte ein wahrheitsgetreues Bild einiger diskutierter Punkte vermitteln, mit weniger persönlicher Meinung. Ein paar Punkte sind besonders hervorzuheben:

  1. Eine so anfällige Frage, um in die eigene Meinung zu fallen, braucht eine Antwort mit neutralem Pro und Kontra. Nicht nur Pro oder Contra.

    Eine gute Übersicht über die Vor- und Nachteile ist in dieser Antwort aufgeführt:

    https://stackoverflow.com/a/33047365/12139179

    (Ich persönlich halte dies aus diesem Grund für die bisher beste Antwort).


  1. Ein Grund, auf den man höchstens stößt, um das Weglassen des Abdrucks zu begründen, ist, dass der Abdruck einen Fehler verbergen könnte.

    Wenn jemand eine implizit deklarierte malloc() die zurückgibt int (implizite Funktionen sind seit C99 aus dem Standard verschwunden) und sizeof(int) != sizeof(int*) wie in dieser Frage gezeigt

    Warum ist dieser Code auf der 64-Bit-Architektur fehlerhaft, funktioniert aber auf der 32-Bit-Architektur problemlos?

    der Guss würde einen Fehler verbergen.

    Dies ist zwar richtig, zeigt aber nur die Hälfte der Geschichte, denn das Weglassen der Besetzung wäre nur eine vorwärtsbringende Lösung für einen noch größeren Fehler - die Nichtberücksichtigung stdlib.h bei Verwendung von malloc() .

    Dies wird nie ein ernstes Problem sein, wenn Sie,

    1. Verwenden Sie einen Compiler, der C99 oder höher kompiliert (was empfohlen wird und obligatorisch sein sollte), und

    2. Sie sind nicht so abwesend, dass Sie vergessen hätten, die stdlib.h verwenden, wenn Sie malloc() in Ihrem Code, was an sich schon ein großer Fehler ist.


  1. Einige Leute streiten über die C++-Konformität von C-Code, da der Cast in C++ obligatorisch ist.

    Zunächst einmal ganz allgemein zu sagen: Das Kompilieren von C-Code mit einem C++-Compiler ist keine gute Praxis.

    C und C++ sind in der Tat zwei völlig unterschiedliche Sprachen mit unterschiedlicher Semantik.

    Aber wenn Sie wirklich wollen/brauchen, um C-Code kompatibel zu C++ und umgekehrt zu machen, verwenden Sie Compiler-Switches anstelle von Casts.

    Da die Besetzung tendenziell als überflüssig oder sogar schädlich deklariert wird, möchte ich mich auf diese Fragen konzentrieren, die gute Gründe dafür liefern, warum die Besetzung nützlich oder sogar notwendig sein kann:


  1. Der Cast kann unvorteilhaft sein, wenn sich Ihr Code bzw. der Typ des zugewiesenen Zeigers (und damit der Typ des Casts) ändert, obwohl dies in den meisten Fällen unwahrscheinlich ist. Dann müssten Sie auch alle Casts pflegen/ändern und wenn Sie ein paar tausend Aufrufe von Speicherverwaltungsfunktionen in Ihrem Code haben, kann sich das wirklich summieren und die Wartungseffizienz verringern.

Zusammenfassung:

Tatsache ist, dass der Cast nach dem C-Standard (bereits seit ANSI-C (C89/C90)) überflüssig ist, wenn der zugewiesene Zeiger auf ein Objekt mit fundamentaler Ausrichtungsanforderung zeigt (was die meisten aller Objekte umfasst).

Sie müssen den Cast nicht durchführen, da der Zeiger in diesem Fall automatisch ausgerichtet wird:

"Die Reihenfolge und Zusammengehörigkeit des durch aufeinanderfolgende Aufrufe der Funktionen aligned_alloc, calloc, malloc und realloc zugewiesenen Speichers ist nicht spezifiziert. Der Zeiger, der bei erfolgreicher Zuweisung zurückgegeben wird, ist entsprechend ausgerichtet, so dass er einem Zeiger auf einen beliebigen Objekttyp mit einer grundlegenden Ausrichtungsanforderung zugewiesen werden kann und dann verwendet werden, um auf ein solches Objekt oder ein Array solcher Objekte in dem zugewiesenen Bereich zuzugreifen (bis der Bereich explizit freigegeben wird)".

Quelle: C18, §7.22.3/1


"A grundsätzliche Ausrichtung ist eine gültige Ausrichtung, die kleiner oder gleich ist _Alignof (max_align_t) . Grundlegende Ausrichtungen müssen von der Implementierung für Objekte aller Speicherdauern unterstützt werden. Die Ausrichtungsanforderungen der folgenden Typen müssen grundlegende Ausrichtungen sein:

- alle atomaren, qualifizierten oder unqualifizierten Basistypen;

- alle atomaren, qualifizierten oder unqualifizierten Aufzählungstypen;

- alle atomaren, qualifizierten oder unqualifizierten Zeigertypen;

- alle Array-Typen, deren Elementtyp eine grundlegende Ausrichtungsanforderung hat;57)

- alle in Klausel 7 genannten Typen als vollständige Objekttypen;

- alle Struktur- oder Vereinigungstypen, deren Elemente alle Typen mit grundlegenden Ausrichtungsanforderungen haben und von denen keines einen Ausrichtungsspezifizierer hat, der eine Ausrichtung angibt, die keine grundlegende Ausrichtung ist.

  1. Wie in Abschnitt 6.2.1 beschrieben, kann die spätere Erklärung die frühere Erklärung verdecken.

Quelle: C18, §6.2.8/2

Wenn Sie jedoch Speicher für ein implementierungsdefiniertes Objekt mit erweiterter Ausrichtungsanforderung zuweisen, ist der Cast erforderlich.

Un erweiterte Ausrichtung wird durch eine Ausrichtung dargestellt, die größer ist als _Alignof (max_align_t) . Es ist implementierungsabhängig, ob erweiterte Ausrichtungen unterstützt werden und für welche Speicherdauern sie unterstützt werden. Ein Typ mit einer erweiterten Ausrichtungsanforderung ist ein überalignierter Typ.58)

Quelle. C18, §6.2.8/3

Alles andere ist eine Frage des konkreten Anwendungsfalls und der eigenen Meinung.

Bitte seien Sie vorsichtig, wie Sie sich bilden.

Ich empfehle Ihnen zu lesen alle der bisher gegebenen Antworten (sowie deren Kommentare, die auf einen Misserfolg hindeuten können) sorgfältig durch und bilden Sie sich dann Ihre eigene Meinung, ob Sie das Ergebnis der malloc() in einem bestimmten Fall.

Bitte beachten:

Auf diese Frage gibt es keine richtige oder falsche Antwort. Es ist eine Frage des Stils, und Sie selbst entscheiden, welchen Weg Sie wählen (wenn Sie nicht durch Ausbildung oder Beruf dazu gezwungen sind). Bitte seien Sie sich dessen bewusst und lassen Sie sich nicht täuschen .


Letzte Anmerkung: Ich habe dafür gestimmt, diese Frage kürzlich als meinungsbasiert zu schließen, was in der Tat seit Jahren notwendig ist. Wenn Sie das Privileg haben, die Frage zu schließen oder wieder zu öffnen, möchte ich Sie bitten, dies ebenfalls zu tun.

4 Stimmen

Ich unterstütze diese Aussage voll und ganz.

2 Stimmen

Das ist so ziemlich dasselbe, was in dieser alten Antwort steht: stackoverflow.com/a/22538350/584518 .

1 Stimmen

@Lundin Sie müssen den falschen Link eingefügt haben, denn diese Antwort hat nichts mit dieser Antwort zu tun, so scheint es.

24voto

Leute, die an GCC und Clang gewöhnt sind, sind verwöhnt. Es ist nicht alles so gut da draußen.

Ich war im Laufe der Jahre ziemlich entsetzt über die erstaunlich veralteten Compiler, die ich verwenden musste. Oftmals verfolgen Unternehmen und Manager einen extrem konservativen Ansatz beim Wechsel von Compilern und wollen nicht einmal Test ob ein neuer Compiler (mit besserer Einhaltung von Standards und Code-Optimierung) in ihrem System funktionieren wird. Die praktische Realität für Entwickler ist, dass man sich beim Programmieren absichern muss, und leider ist das Casting von Mallocs eine gute Angewohnheit, wenn man nicht kontrollieren kann, welcher Compiler auf den eigenen Code angewendet wird.

Ich würde auch vorschlagen, dass viele Organisationen einen eigenen Kodierungsstandard anwenden und dass dass sollte die Methode sein, der man folgt, wenn sie definiert ist. In Ermangelung einer expliziten Anleitung neige ich dazu, die Methode zu wählen, die sich am ehesten überall kompilieren lässt, anstatt mich sklavisch an eine Norm zu halten.

Das Argument, dass dies bei den derzeitigen Standards nicht notwendig ist, ist durchaus berechtigt. Aber dieses Argument geht an den praktischen Gegebenheiten in der realen Welt vorbei. Wir programmieren nicht in einer Welt, die ausschließlich von der aktuellen Norm bestimmt wird, sondern von den praktischen Gegebenheiten dessen, was ich gerne als "Realitätsfeld des lokalen Managements" bezeichne. Und das ist stärker verbogen und verdreht, als es die Raumzeit je war :-)

YMMV.

Ich neige dazu, den Aufruf von malloc als defensive Operation zu betrachten. Nicht schön, nicht perfekt, aber im Allgemeinen sicher. ( Ehrlich gesagt, wenn Sie stdlib.h nicht eingebunden haben, dann haben Sie Weg mehr Probleme als mit malloc ! ).

22voto

Nein, man wirft nicht das Ergebnis von malloc() .

Im Allgemeinen müssen Sie *werfen nicht nach oder von `void `** .

Ein typischer Grund, der dafür angeführt wird, ist, dass das Versäumnis, dies zu tun #include <stdlib.h> könnte unbemerkt bleiben. Dies ist seit langem kein Problem mehr, da C99 die implizite Funktionsdeklarationen illegal, d.h. wenn Ihr Compiler mindestens mit C99 konform ist, erhalten Sie eine Diagnosemeldung.

Aber es gibt eine viel stärkerer Grund keine unnötigen Zeigerübertragungen einzuführen:

In C wird ein Pointer Cast ist fast immer ein Fehler . Dies ist auf die folgende Regel zurückzuführen ( §6.5 p7 in N1570, dem letzten Entwurf für C11):

Auf den gespeicherten Wert eines Objekts darf nur durch einen lvalue-Ausdruck zugegriffen werden, der eine der folgenden Eigenschaften hat der folgenden Typen hat:
- einen Typ, der mit dem effektiven Typ des Objekts kompatibel ist,
- eine qualifizierte Version eines Typs, der mit dem tatsächlichen Typ des Objekts kompatibel ist,
- einen Typ, der dem vorzeichenbehafteten oder vorzeichenlosen Typ entspricht, der dem effektiven Typ des Objekts entspricht,
- einen Typ, der der Typ mit oder ohne Vorzeichen ist, der einer qualifizierten Version des effektiven Typs des Objekts entspricht,
- ein Aggregat- oder Vereinigungstyp, der einen der oben genannten Typen unter seinen Typen enthält enthält (einschließlich, rekursiv, ein Mitglied eines Unteraggregats oder einer enthaltenen Vereinigung), oder
- einen Zeichentyp.

Dies ist auch bekannt als die strenge Aliasing-Regel . Der folgende Code lautet also undefiniertes Verhalten :

long x = 5;
double *p = (double *)&x;
double y = *p;

Und, manchmal überraschend, auch die folgenden:

struct foo { int x; };
struct bar { int x; int y; };
struct bar b = { 1, 2};
struct foo *p = (struct foo *)&b;
int z = p->x;

Manchmal muss man faire müssen Zeiger gecastet werden, aber angesichts der strenge Aliasing-Regel muss man sehr vorsichtig damit sein. Jedes Vorkommen eines Zeiger-Casts in Ihrem Code ist also ein Ort, an dem Sie die Gültigkeit zweimal überprüfen müssen . Daher sollten Sie niemals einen unnötigen Zeiger-Cast schreiben.

tl;dr

Kurz und bündig: Weil in C, cualquier Auftreten eines Zeigerwurf eine rote Fahne für Code, der besondere Aufmerksamkeit erfordert, sollten Sie niemals schreiben unnötig Zeigerwürfe.


Randnotizen:

  • Es gibt Fälle, in denen Sie tatsächlich brauchen eine Besetzung von void * z.B. wenn Sie einen Zeiger ausdrucken wollen:

    int x = 5;
    printf("%p\n", (void *)&x);

    Die Besetzung ist hier notwendig, weil printf() ist eine variadische Funktion, so dass implizite Konvertierungen nicht möglich sind.

  • In C++ ist die Situation anders. Das Casting von Zeigertypen ist beim Umgang mit Objekten abgeleiteter Klassen durchaus üblich (und korrekt). Daher ist es sinnvoll, dass in C++ die Konvertierung in und aus void * es no implizit. In C++ gibt es eine ganze Reihe verschiedener Varianten des Castings.

1 Stimmen

In Ihren Beispielen vermeiden Sie void *. Es gibt einen Unterschied zwischen cast von double * zu int * und umgekehrt. malloc gibt pointel ausgerichtet auf den größten Standardtyp zurück, so dass keine Aliasing-Regeln gebrochen werden, selbst wenn jemand casts von diesem ausgerichteten Zeiger auf einen anderen Typ.

0 Stimmen

Aliasing hat nichts und der Rest Ihres Kommentars - Sie haben offensichtlich nicht verstanden, worum es geht.

0 Stimmen

@PeterJ: Nur für den Fall, dass es darum geht, die vermeiden einen unnötigen Zeiger-Cast, damit er nicht aussehen wie ein Teil des Codes, dem Sie besondere Aufmerksamkeit schenken müssen.

18voto

Kaz Punkte 51547

Ich habe den Cast nur eingefügt, um zu zeigen, dass ich die hässliche Lücke im Typsystem missbillige, die es ermöglicht, dass Code wie der folgende Schnipsel ohne Diagnose kompiliert werden kann, obwohl keine Casts verwendet werden, um die schlechte Konvertierung zu verursachen:

double d;
void *p = &d;
int *q = p;

Ich wünschte, das gäbe es nicht (und in C++ gibt es das auch nicht), und so habe ich gecastet. Es repräsentiert meinen Geschmack und meine Programmierpolitik. Ich werfe nicht nur einen Zeiger, sondern gebe tatsächlich einen Stimmzettel ab, und Dämonen der Dummheit austreiben . Wenn ich nicht kann eigentlich die Dummheit austreiben dann lassen Sie mich wenigstens den Wunsch dazu mit einer Geste des Protests zum Ausdruck bringen.

In der Tat ist es eine gute Praxis, die malloc (und Freunde) mit Funktionen, die Folgendes zurückgeben unsigned char * und grundsätzlich nie zu verwenden void * in Ihrem Code. Wenn Sie einen generischen Zeiger auf ein beliebiges Objekt benötigen, verwenden Sie eine char * o unsigned char * und haben Würfe in beide Richtungen. Die einzige Entspannung, die man sich gönnen kann, ist vielleicht die Verwendung von Funktionen wie memset y memcpy ohne Gipsabdrücke.

Zum Thema Casting und C++-Kompatibilität: Wenn Sie Ihren Code so schreiben, dass er sowohl in C als auch in C++ kompilierbar ist (in diesem Fall müssen Sie müssen wird der Rückgabewert von malloc wenn sie etwas anderem zugewiesen wird als void * ), können Sie eine sehr hilfreiche Sache für sich selbst tun: Sie können Makros für Casts verwenden, die zu C++-Stil Casts übersetzen, wenn als C++ kompiliert, aber zu einem C-Cast reduzieren, wenn als C kompiliert:

/* In a header somewhere */
#ifdef __cplusplus
#define strip_qual(TYPE, EXPR) (const_cast<TYPE>(EXPR))
#define convert(TYPE, EXPR) (static_cast<TYPE>(EXPR))
#define coerce(TYPE, EXPR) (reinterpret_cast<TYPE>(EXPR))
#else
#define strip_qual(TYPE, EXPR) ((TYPE) (EXPR))
#define convert(TYPE, EXPR) ((TYPE) (EXPR))
#define coerce(TYPE, EXPR) ((TYPE) (EXPR))
#endif

Wenn Sie sich an diese Makros halten, dann ist eine einfache grep Die Suche nach diesen Bezeichnern in Ihrer Codebasis zeigt Ihnen, wo sich alle Besetzungen befinden, so dass Sie überprüfen können, ob eine von ihnen falsch ist.

Wenn Sie den Code dann regelmäßig mit C++ kompilieren, wird er die Verwendung eines geeigneten Casts erzwingen. Zum Beispiel, wenn Sie strip_qual nur zum Entfernen einer const o volatile aber das Programm ändert sich so, dass nun eine Typkonvertierung erforderlich ist, erhalten Sie eine Diagnose, und Sie müssen eine Kombination von Casts verwenden, um die gewünschte Konvertierung zu erhalten.

Um Ihnen zu helfen, diese Makros einzuhalten, hat der GNU C++ (nicht C!) Compiler eine wunderbare Funktion: eine optionale Diagnose, die für alle Vorkommen von C-Style Casts erzeugt wird.

     -Wold-style-cast (C++ and Objective-C++ only)
         Warn if an old-style (C-style) cast to a non-void type is used
         within a C++ program.  The new-style casts (dynamic\_cast,
         static\_cast, reinterpret\_cast, and const\_cast) are less vulnerable
         to unintended effects and much easier to search for.

Wenn Ihr C-Code als C++ kompiliert wird, können Sie Folgendes verwenden -Wold-style-cast um alle Vorkommen des Wortes (type) Gießsyntax, die sich in den Code einschleichen kann, und folgen Sie diesen Diagnosen, indem Sie sie durch eine geeignete Auswahl aus den oben genannten Makros (oder eine Kombination, falls erforderlich) ersetzen.

Diese Behandlung von Konvertierungen ist die größte eigenständige technische Rechtfertigung für das Arbeiten in "Clean C": dem kombinierten C- und C++-Dialekt, der wiederum technisch das Casting des Rückgabewerts von malloc .

0 Stimmen

Wie bereits von anderen erwähnt, würde ich normalerweise empfehlen, C- und C++-Code nicht zu mischen. Wenn Sie jedoch einen guten Grund dafür haben, dann können Makros nützlich sein.

0 Stimmen

@Phil1970 Es ist alles in einem zusammenhängenden Dialekt geschrieben, der zufällig auf C- und C++-Compiler portierbar ist und einige Fähigkeiten von C++ nutzt. Es muss alles als C++ kompiliert werden, oder aber alles als C.

0 Stimmen

D.h., was ich mit meinem vorherigen Kommentar sagen wollte, ist, dass es keine Vermischung von C und C++ gibt. Die Absicht ist, dass der gesamte Code als C oder als C++ kompiliert wird.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X