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

2534voto

unwind Punkte 377331

TL;DR

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

hat zwei Probleme. Der Cast und die Tatsache, dass Sie den Typ statt der Variable als Argument für sizeof verwenden. Machen Sie stattdessen Folgendes:

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

Lange Fassung

Nein ; Sie nicht werfen das Ergebnis, da:

  • Das ist unnötig, denn void * wird in diesem Fall automatisch und sicher in jeden anderen Zeigertyp umgewandelt.
  • Der Code wird dadurch unübersichtlicher, und Abwandlungen sind nicht sehr leicht zu lesen (insbesondere wenn der Zeigertyp lang ist).
  • Das führt dazu, dass man sich wiederholt, was im Allgemeinen schlecht ist.
  • Es kann einen Fehler verbergen, wenn Sie vergessen haben, die <stdlib.h> . Dies kann zu Abstürzen führen (oder, schlimmer noch, no erst viel später in einem völlig anderen Teil des Codes einen Absturz verursachen). Bedenken Sie, was passiert, wenn Zeiger und Ganzzahlen unterschiedlich groß sind; dann verstecken Sie eine Warnung durch Casting und verlieren möglicherweise Bits der zurückgegebenen Adresse. Anmerkung: seit C99 gibt es keine impliziten Funktionen mehr in C, und dieser Punkt ist nicht mehr relevant, da es keine automatische Annahme mehr gibt, dass nicht deklarierte Funktionen zurückgeben int .

Zur Klarstellung: Ich sagte "man wirft nicht", nicht "man wirft nicht brauchen zu werfen". Meiner Meinung nach ist es ein Versäumnis, den Wurf einzubeziehen, selbst wenn man ihn richtig gemacht hat. Es gibt einfach keine Vorteile, aber einen Haufen potenzieller Risiken, und die Einbeziehung des Gipses zeigt, dass Sie die Risiken nicht kennen.

Beachten Sie auch, wie Kommentatoren anmerken, dass es sich hier um reines C handelt, nicht um C++. Ich glaube fest an C und C++ als getrennte Sprachen.

Hinzu kommt, dass Ihr Code die Typinformationen unnötigerweise wiederholt ( int ), was zu Fehlern führen kann. Es ist besser, den Zeiger, der zum Speichern des Rückgabewerts verwendet wird, zu de-referenzieren, um die beiden zusammen zu "sperren":

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

Dadurch wird auch die length nach vorne, um die Sichtbarkeit zu erhöhen, und lässt die überflüssigen Klammern mit sizeof ; sie werden nur benötigt wenn das Argument ein Typname ist. Viele Leute scheinen dies nicht zu wissen (oder zu ignorieren), was ihren Code noch langatmiger macht. Zur Erinnerung: sizeof ist keine Funktion! :)


Während des Umzugs length nach vorne Mai die Sichtbarkeit in einigen seltenen Fällen zu erhöhen, sollte man auch darauf achten, dass es im allgemeinen Fall besser ist, den Ausdruck als zu schreiben:

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

Da die Beibehaltung der sizeof In diesem Fall wird zunächst sichergestellt, dass die Multiplikation mit mindestens size_t math.

Vergleichen Sie: malloc(sizeof *sieve * length * width) vs. malloc(length * width * sizeof *sieve) die zweite kann die length * width wenn width y length sind kleinere Typen als size_t .

60 Stimmen

Bitte denken Sie daran, die Antwort zu aktualisieren. Der Wurf ist nicht mehr gefährlich, und sich zu wiederholen ist nicht unbedingt etwas Schlechtes (Redundanz kann helfen, Fehler zu erkennen).

36 Stimmen

Compiler haben sich verändert. Ein aktueller Compiler wird Sie vor einer fehlenden Deklaration von malloc warnen.

107 Stimmen

@n.m. Ok. Ich denke, es ist schlecht, davon auszugehen, dass jeder, der hier liest, einen bestimmten Compiler hat. Außerdem ist seit C11 das gesamte Konzept der "impliziten Funktion" verschwunden, das wusste ich nicht. Trotzdem sehe ich keinen Sinn darin, einen sinnlosen Cast hinzuzufügen. Machst du auch int x = (int) 12; nur um die Dinge klarzustellen?

480voto

dirkgently Punkte 104289

In C müssen Sie den Rückgabewert von malloc . Der Zeiger auf void, der von malloc wird automatisch in den richtigen Typ umgewandelt. Wenn Sie jedoch möchten, dass Ihr Code mit einem C++-Compiler kompiliert werden kann, ist ein Cast erforderlich. Eine in der Community bevorzugte Alternative ist die Verwendung des Folgenden:

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

was Sie zusätzlich davon befreit, sich um die Änderung der rechten Seite des Ausdrucks kümmern zu müssen, wenn Sie jemals den Typ von sieve .

Die Besetzung ist schlecht, wie schon gesagt wurde. Besonders Zeigerwürfe.

126 Stimmen

@MAKZ Ich würde behaupten, dass malloc(length * sizeof *sieve) lässt es so aussehen sizeof ist eine Variable - ich denke also malloc(length * sizeof(*sieve)) ist besser lesbar.

36 Stimmen

Und malloc(length * (sizeof *sieve)) noch lesbarer. IMHO.

25 Stimmen

@Michael Anderson () Problem beiseite, beachten Sie, dass Ihr vorgeschlagener Stil die Reihenfolge geändert hat, wenn die Elementanzahl wie folgt berechnet wird length*width , wobei die sizeof Die erste Zahl in diesem Fall gewährleistet, dass die Multiplikation mit mindestens size_t math. Vergleichen Sie malloc(sizeof( *ptr) * length * width) vs. malloc(length * width * sizeof (*ptr)) - die 2. kann überlaufen die length*width cuando width,length sind kleinere Typen, die size_t .

410voto

Ron Burk Punkte 5705

Sie faire Besetzung, weil:

  • Es macht Ihren Code tragbarer zwischen C und C++, und wie die Erfahrung von SO zeigt, behaupten viele Programmierer, sie würden in C schreiben, obwohl sie in Wirklichkeit in C++ (oder C plus lokale Compiler-Erweiterungen) schreiben.
  • Dies zu unterlassen kann einen Fehler verbergen : Beachten Sie all die SO-Beispiele für die Verwirrung beim Schreiben type * gegen type ** .
  • Der Gedanke, dass es Sie davor bewahrt, zu bemerken, dass Sie es versäumt haben #include eine entsprechende Header-Datei vermisst den Wald vor lauter Bäumen nicht sehen . Das ist dasselbe, als würde man sagen: "Mach dir keine Gedanken darüber, dass du den Compiler nicht gebeten hast, sich darüber zu beschweren, dass er keine Prototypen sieht - die lästige stdlib.h ist das WIRKLICH Wichtige, das man sich merken muss!"
  • Sie erzwingt eine zusätzliche kognitive Gegenkontrolle . Es setzt den (vermeintlich) gewünschten Typ direkt neben die Arithmetik, die Sie für die Rohgröße dieser Variablen durchführen. Ich wette, Sie könnten eine SO-Studie erstellen, die zeigt, dass malloc() Käfer werden viel schneller gefangen, wenn sie geworfen werden. Wie bei Assertions verringern Anmerkungen, die die Absicht offenbaren, Fehler.
  • Sich in einer Weise zu wiederholen, die die Maschine überprüfen kann, ist oft eine großartig Idee. In der Tat ist das eine Behauptung, und diese Verwendung von "cast" ist eine Behauptung. Behauptungen sind immer noch die allgemeinste Technik, die wir haben, um korrekten Code zu erhalten, seit Turing vor so vielen Jahren auf diese Idee kam.

41 Stimmen

@ulidtko Falls Sie es noch nicht wussten: Es ist möglich, Code zu schreiben, der sich sowohl als C als auch als C++ kompilieren lässt. In der Tat sind die meisten Header-Dateien so, und sie enthalten oft Code (Makros und Inline-Funktionen). Mit einer .c / .cpp Datei als beides zu kompilieren, ist nicht sehr oft nützlich, aber ein Fall ist das Hinzufügen von C++ throw Unterstützung bei der Kompilierung mit einem C++-Compiler (aber return -1; wenn sie mit einem C-Compiler kompiliert werden).

43 Stimmen

Wenn jemand malloc-Aufrufe inline in einem Header hätte, wäre ich nicht beeindruckt, #ifdef __cplusplus und extern "C" {} sind für diesen Job, nicht für das Hinzufügen von zusätzlichen Casts.

3 Stimmen

Außerdem erhalten Sie Kompatibilität mit Compilern, die C standardmäßig als C++ kompilieren. (<hust>MSVC</hust>)

199voto

quinmars Punkte 10775

Wie bereits erwähnt, ist es für C nicht erforderlich, aber für C++ notwendig. Wenn Sie denken, dass Sie Ihren C-Code mit einem C++-Compiler kompilieren werden, aus welchen Gründen auch immer, können Sie stattdessen ein Makro verwenden, wie:

#ifdef __cplusplus
# define NEW(type, count) ((type *)calloc(count, sizeof(type)))
#else
# define NEW(type, count) (calloc(count, sizeof(type)))
#endif

Auf diese Weise können Sie ihn trotzdem sehr kompakt schreiben:

int *sieve = NEW(int, 1);

und es wird für C und C++ kompiliert.

22 Stimmen

Da Sie ohnehin ein Makro verwenden, können Sie auch new in der Definition von C++?

78 Stimmen

Denn es gibt keinen Grund, dies zu tun. Es ist hauptsächlich für C-Programme gedacht, die mit einem C++-Compiler kompiliert werden. Wenn Sie 'new' verwenden, bekommen Sie nur Probleme. Sie brauchen dann auch ein Makro für free. Und man braucht ein Makro, um ein Array freizugeben, eine Unterscheidung, die es in C nicht gibt.

9 Stimmen

Ganz zu schweigen davon, ob nicht Sie selbst den Speicher freigeben, sondern vielleicht eine von Ihnen verwendete C-Bibliothek usw. Viele mögliche Probleme ohne jeglichen Nutzen.

174voto

ashiquzzaman33 Punkte 5581

Von der Wikipedia :

Vorteile des Gießens

  • Die Einbeziehung des Casts kann es ermöglichen, ein C-Programm oder eine Funktion als C++ zu kompilieren.

  • Der Cast ermöglicht die Verwendung von Versionen von malloc aus der Zeit vor 1989, die ursprünglich ein char * zurückgaben.

  • Das Casting kann dem Entwickler dabei helfen, Inkonsistenzen bei der Größenbestimmung des Typs zu erkennen, wenn sich der Typ des Zielzeigers ändert, insbesondere wenn der Zeiger weit entfernt vom malloc()-Aufruf deklariert ist (obwohl moderne Compiler und statische Analysatoren vor einem solchen Verhalten warnen können, ohne dass das Casting erforderlich ist).

Nachteile des Gießens

  • Nach dem ANSI C-Standard ist die Besetzung überflüssig.

  • Das Hinzufügen der Besetzung kann das Versäumnis verdecken, die Kopfzeile aufzunehmen stdlib.h , in in dem der Prototyp für malloc zu finden ist. In Ermangelung eines Prototyp für malloc, verlangt der Standard, dass der C-Compiler annimmt, dass malloc einen int zurückgibt. Wenn es keinen Cast gibt, wird eine Warnung ausgegeben Warnung ausgegeben, wenn diese ganze Zahl dem Zeiger zugewiesen wird; mit wird diese Warnung jedoch nicht ausgegeben, was einen Fehler verbirgt. Auf bestimmten Architekturen und Datenmodellen (z. B. LP64 auf 64-Bit-Systemen, wo long und Zeiger 64-Bit und int 32-Bit sind), kann dieser Fehler tatsächlich zu undefiniertem Verhalten führen, da das implizit deklarierte malloc einen 32-Bit-Wert zurückgibt, während die tatsächlich definierte Funktion einen 64-Bit-Wert zurückgibt. Je nach Aufrufkonventionen und Speicher Layout kann dies zu einem Stack Smashing führen. Dieses Problem ist weniger wahrscheinlich modernen Compilern weniger wahrscheinlich unbemerkt bleiben, da sie einheitlich Warnungen ausgeben, dass eine nicht deklarierte Funktion verwendet wurde, so dass eine Warnung immer noch erscheinen. Das Standardverhalten von GCC ist zum Beispiel, eine Warnung mit dem Wortlaut "inkompatible implizite Deklaration einer eingebauten Funktion", unabhängig davon, ob der Cast vorhanden ist oder nicht.

  • Wenn der Typ des Zeigers bei seiner Deklaration geändert wird, kann man auch alle Zeilen ändern, in denen malloc aufgerufen und gecastet wird.

Obwohl malloc ohne Casting ist die bevorzugte Methode und die meisten erfahrenen Programmierer wählen sie Sie sollten das verwenden, was Ihnen gefällt, wenn Sie sich der Problematik bewusst sind.

d.h.: Wenn Sie ein C-Programm als C++ kompilieren müssen (obwohl es eine eigene Sprache ist), müssen Sie das Ergebnis der Verwendung malloc .

3 Stimmen

Was bedeutet " Das Casting kann dem Entwickler helfen, Inkonsistenzen in der Typisierung zu erkennen, wenn sich der Typ des Zielzeigers ändert, insbesondere wenn der Zeiger weit von der malloc() aufrufen " bedeuten? Könnten Sie ein Beispiel nennen?

5 Stimmen

@CoolGuy: Siehe einen früheren Kommentar zu einer anderen Antwort . Aber beachten Sie, dass die p = malloc(sizeof(*p) * count) Idiom erkennt Änderungen des Typs automatisch, so dass Sie keine Warnungen erhalten und nichts ändern müssen. Dies ist also kein wirklicher Vorteil gegenüber der besten Alternative für Nicht-Casting.

11 Stimmen

Dies ist die richtige Antwort: Es gibt Vor- und Nachteile, und es ist letztlich eine Frage des Geschmacks (es sei denn, der Code muss als C++ kompiliert werden - dann ist der Cast obligatorisch).

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