Werfe ich das Ergebnis von Malloc?

Mar 03 2009

In dieser Frage schlug jemand in einem Kommentar vor, dass ich das Ergebnis von nicht besetzen sollte malloc, dh

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

eher, als:

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

Warum sollte das so sein?

Antworten

2285 unwind Mar 03 2009 at 17:17

Nein ; Sie geben das Ergebnis nicht aus, da:

  • void *Dies ist nicht erforderlich, da in diesem Fall automatisch und sicher auf einen anderen Zeigertyp hochgestuft wird.
  • Es fügt dem Code Unordnung hinzu, Casts sind nicht sehr einfach zu lesen (insbesondere wenn der Zeigertyp lang ist).
  • Es bringt dich dazu, dich zu wiederholen, was im Allgemeinen schlecht ist.
  • Es kann einen Fehler verbergen, wenn Sie vergessen haben, ihn einzuschließen <stdlib.h>. Dies kann dazu führen , Abstürze (oder, schlimmer noch, nicht zu einem Absturz bis Weg führt später in einem völlig anderen Teil des Codes). Überlegen Sie, was passiert, wenn Zeiger und Ganzzahlen unterschiedlich groß sind. Dann verstecken Sie eine Warnung durch Casting und verlieren möglicherweise Teile Ihrer zurückgegebenen Adresse. Hinweis: Ab C99 sind implizite Funktionen von C verschwunden, und dieser Punkt ist nicht mehr relevant, da nicht automatisch davon ausgegangen wird, dass nicht deklarierte Funktionen zurückgegeben werden int.

Als Klarstellung, dass Notiz sagte ich , „du nicht werfen“, nicht „Sie nicht brauchen , um cast“. Meiner Meinung nach ist es ein Fehler, die Besetzung einzubeziehen, selbst wenn Sie es richtig verstanden haben. Es hat einfach keine Vorteile, aber eine Reihe potenzieller Risiken und die Einbeziehung der Besetzung weisen darauf hin, dass Sie nicht über die Risiken Bescheid wissen.

Beachten Sie auch, wie Kommentatoren hervorheben, dass in den obigen Abschnitten nur C und nicht C ++ behandelt wird. Ich glaube fest an C und C ++ als getrennte Sprachen.

Um weitere Informationen hinzuzufügen, wiederholt Ihr Code unnötigerweise die Typinformationen ( int), die Fehler verursachen können. 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 lengthnach vorne verschoben, um die Sichtbarkeit zu verbessern, und die redundanten Klammern werden mit sizeofgelöscht. Sie werden nur benötigt, wenn das Argument ein Typname ist. Viele Leute scheinen dies nicht zu wissen (oder zu ignorieren), was ihren Code ausführlicher macht. Denken Sie daran: sizeofist keine Funktion! :) :)


Während das Bewegen lengthnach vorne in einigen seltenen Fällen die Sichtbarkeit erhöhen kann , sollte man auch darauf achten, dass es im allgemeinen Fall besser sein sollte, den Ausdruck wie folgt zu schreiben:

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

Da die sizeoferste beibehalten wird , wird in diesem Fall sichergestellt, dass die Multiplikation mindestens mit size_tMathematik erfolgt.

Vergleichen: malloc(sizeof *sieve * length * width)gegen malloc(length * width * sizeof *sieve)die Sekunde kann das length * widthWann überlaufen widthund lengthsind kleinere Typen als size_t.

387 dirkgently Mar 03 2009 at 17:17

In C müssen Sie den Rückgabewert von nicht umwandeln malloc. Der von zurückgegebene Zeiger auf void mallocwird automatisch in den richtigen Typ konvertiert. Wenn Sie jedoch möchten, dass Ihr Code mit einem C ++ - Compiler kompiliert wird, ist eine Umwandlung erforderlich. Eine bevorzugte Alternative in der Community ist die Verwendung der folgenden Optionen:

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

Dies erspart Ihnen außerdem die Sorge, die rechte Seite des Ausdrucks zu ändern, wenn Sie jemals den Typ ändern sieve.

Casts sind schlecht, wie die Leute betont haben. Besonders Zeigerabdrücke.

360 RonBurk Feb 14 2013 at 23:15

Sie haben werfen, denn:

  • Dadurch wird Ihr Code zwischen C und C ++ portabler , und wie die SO-Erfahrung zeigt, behaupten sehr viele Programmierer, dass sie in C schreiben, wenn sie wirklich in C ++ (oder C plus lokale Compiler-Erweiterungen) schreiben.
  • Andernfalls kann ein Fehler ausgeblendet werden : Beachten Sie alle SO-Beispiele für Verwirrung beim Schreiben im type *Vergleich zu type **.
  • Die Idee, dass Sie nicht bemerken, dass Sie #includeeine entsprechende Header-Datei nicht gefunden haben, verfehlt den Wald für die Bäume . Es ist das gleiche wie zu sagen: "Mach dir keine Sorgen darüber, dass du den Compiler nicht gebeten hast, dich darüber zu beschweren, dass du keine Prototypen siehst - diese lästige stdlib.h ist die WIRKLICH wichtige Sache, an die du dich erinnern musst!"
  • Es erzwingt eine zusätzliche kognitive Gegenprüfung . Der (angeblich) gewünschte Typ wird direkt neben die Arithmetik gesetzt, die Sie für die Rohgröße dieser Variablen ausführen. Ich wette, Sie könnten eine SO-Studie durchführen, die zeigt, dass malloc()Fehler bei einer Besetzung viel schneller erkannt werden. Wie bei Behauptungen verringern Anmerkungen, die Absichten aufdecken, Fehler.
  • Es ist oft eine gute Idee, sich so zu wiederholen, dass die Maschine dies überprüfen kann . In der Tat ist das, was eine Behauptung ist, und diese Verwendung von Besetzung ist eine Behauptung. Behauptungen sind immer noch die allgemeinste Technik, um Code korrekt zu machen, da Turing vor so vielen Jahren auf die Idee gekommen ist.
174 quinmars Mar 03 2009 at 18:17

Wie bereits erwähnt, wird es nicht für C, sondern für C ++ benötigt. Wenn Sie glauben, dass Sie Ihren C-Code aus irgendeinem Grund mit einem C ++ - Compiler kompilieren möchten, können Sie stattdessen ein Makro verwenden, z.

#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 es immer noch sehr kompakt schreiben:

int *sieve = NEW(int, 1);

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

146 ashiquzzaman33 Oct 10 2015 at 04:23

Aus der Wikipedia :

Vorteile beim Gießen

  • Durch das Einschließen der Besetzung kann ein C-Programm oder eine C-Funktion möglicherweise als C ++ kompiliert werden.

  • Die Besetzung erlaubt Versionen von Malloc vor 1989, die ursprünglich ein Zeichen * zurückgegeben haben.

  • Das Casting kann dem Entwickler helfen, Inkonsistenzen bei der Typgröße zu identifizieren, falls sich der Zielzeigertyp ändert, insbesondere wenn der Zeiger weit vom Aufruf malloc () entfernt deklariert ist (obwohl moderne Compiler und statische Analysatoren vor einem solchen Verhalten warnen können, ohne dass das Cast erforderlich ist).

Nachteile beim Gießen

  • Nach dem ANSI C-Standard ist die Besetzung redundant.

  • Durch Hinzufügen der Besetzung wird möglicherweise ein Fehler maskiert, der den Header stdlib.h enthält , in dem sich der Prototyp für malloc befindet. In Ermangelung eines Prototyps für malloc verlangt der Standard, dass der C-Compiler davon ausgeht, dass malloc ein int zurückgibt. Wenn keine Umwandlung vorliegt, wird eine Warnung ausgegeben, wenn diese Ganzzahl dem Zeiger zugewiesen wird. Bei der Besetzung wird diese Warnung jedoch nicht ausgegeben, wodurch ein Fehler ausgeblendet wird. Bei bestimmten Architekturen und Datenmodellen (z. B. LP64 auf 64-Bit-Systemen, bei denen 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 ein 32- Bit zurückgibt. Bitwert, während die tatsächlich definierte Funktion einen 64-Bit-Wert zurückgibt. Abhängig von den Aufrufkonventionen und dem Speicherlayout kann dies zu Stapelzerstörungen führen. Es ist weniger wahrscheinlich, dass dieses Problem bei modernen Compilern unbemerkt bleibt, da sie einheitlich Warnungen ausgeben, dass eine nicht deklarierte Funktion verwendet wurde, sodass weiterhin eine Warnung angezeigt wird. Das Standardverhalten von GCC besteht beispielsweise darin, eine Warnung mit der Aufschrift "Inkompatible implizite Deklaration der integrierten Funktion" anzuzeigen, unabhängig davon, ob die Besetzung vorhanden ist oder nicht.

  • Wenn der Typ des Zeigers bei seiner Deklaration geändert wird, müssen möglicherweise auch alle Zeilen geändert werden, in denen malloc aufgerufen und umgewandelt wird.

Obwohl Malloc ohne Casting die bevorzugte Methode ist und die meisten erfahrenen Programmierer sie wählen , sollten Sie verwenden, was immer Sie möchten, um sich der Probleme bewusst zu sein.

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

108 PaulJWilliams Mar 03 2009 at 17:18

In C können Sie einen voidZeiger implizit in eine andere Art von Zeiger konvertieren , sodass eine Umwandlung nicht erforderlich ist. Die Verwendung eines kann dem gelegentlichen Beobachter nahe legen, dass es einen Grund gibt, warum einer benötigt wird, der irreführend sein kann.

104 Lundin Mar 20 2014 at 22:53

Sie geben das Ergebnis von nicht um malloc, da dies Ihrem Code sinnlose Unordnung verleiht.

Der häufigste Grund, warum Leute das Ergebnis von gießen, mallocist, dass sie sich nicht sicher sind, wie die C-Sprache funktioniert. Das ist ein Warnzeichen: Wenn Sie nicht wissen , wie eine bestimmte Sprache Mechanismus funktioniert, dann nicht zu erraten. Schlagen Sie nach oder fragen Sie nach Stapelüberlauf.

Einige Kommentare:

  • Ein ungültiger Zeiger kann ohne explizite Umwandlung in / von einem anderen Zeigertyp konvertiert werden (C11 6.3.2.3 und 6.5.16.1).

  • C ++ erlaubt jedoch keine implizite Umwandlung zwischen void*und einem anderen Zeigertyp. In C ++ wäre die Besetzung also korrekt gewesen. Aber wenn Sie in C ++ programmieren, sollten Sie verwenden newund nicht malloc(). Und Sie sollten niemals C-Code mit einem C ++ - Compiler kompilieren.

    Wenn Sie sowohl C als auch C ++ mit demselben Quellcode unterstützen müssen, verwenden Sie Compiler-Schalter, um die Unterschiede zu markieren. Versuchen Sie nicht, beide Sprachstandards mit demselben Code zu versehen, da sie nicht kompatibel sind.

  • Wenn ein C-Compiler keine Funktion finden kann, weil Sie vergessen haben, den Header einzuschließen, wird diesbezüglich ein Compiler- / Linker-Fehler angezeigt. Wenn Sie also vergessen haben <stdlib.h>, dies einzuschließen, können Sie Ihr Programm nicht erstellen.

  • Bei alten Compilern, die einer Version des Standards folgen, die älter als 25 Jahre ist, würde das Vergessen der Einbeziehung <stdlib.h>zu gefährlichem Verhalten führen. Denn in diesem alten Standard haben Funktionen ohne sichtbaren Prototyp den Rückgabetyp implizit in konvertiert int. Das mallocexplizite Casting des Ergebnisses würde diesen Fehler dann verbergen.

    Aber das ist wirklich kein Problem. Sie verwenden keinen 25 Jahre alten Computer. Warum sollten Sie also einen 25 Jahre alten Compiler verwenden?

95 EFraim Mar 03 2009 at 17:16

In C erhalten Sie eine implizite Konvertierung von void *einem beliebigen anderen (Daten-) Zeiger.

72 YuHao Jun 10 2013 at 00:31

Das Umwandeln des von zurückgegebenen Werts malloc()ist jetzt nicht erforderlich, aber ich möchte einen Punkt hinzufügen, auf den anscheinend niemand hingewiesen hat:

In den alten Tagen, dh bevor ANSI Cvoid * den generischen Zeigertyp bereitstellt , char *ist dies der Typ für eine solche Verwendung. In diesem Fall kann der Cast die Compiler-Warnungen herunterfahren.

Referenz: C FAQ

55 user3079666 Mar 29 2014 at 01:21

Wenn ich nur meine Erfahrung hinzufüge und Computertechnik studiere, sehe ich, dass die zwei oder drei Professoren, die ich in C geschrieben habe, immer Malloc besetzten, aber derjenige, den ich fragte (mit einem immensen Lebenslauf und Verständnis von C), sagte mir, dass es absolut unnötig ist, aber Früher war es nur absolut spezifisch und brachte die Schüler dazu, absolut spezifisch zu sein. Im Wesentlichen ändert das Casting nichts an der Funktionsweise, es macht genau das, was es sagt, weist Speicher zu, und das Casting wirkt sich nicht darauf aus, Sie erhalten denselben Speicher, und selbst wenn Sie ihn versehentlich in etwas anderes umwandeln (und dem Compiler irgendwie ausweichen) Fehler) C greift auf die gleiche Weise darauf zu.

Bearbeiten: Casting hat einen bestimmten Punkt. Wenn Sie die Array-Notation verwenden, muss der generierte Code wissen, wie viele Speicherplätze er vorrücken muss, um den Anfang des nächsten Elements zu erreichen. Dies wird durch Casting erreicht. Auf diese Weise wissen Sie, dass Sie für ein Double 8 Bytes voraus sind, während Sie für ein Int 4 Bytes voraus sind, und so weiter. Wenn Sie die Zeigernotation verwenden, hat dies keine Auswirkung. In der Array-Notation wird dies erforderlich.

53 user968000 Feb 08 2013 at 05:22

Es ist nicht zwingend erforderlich, die Ergebnisse von zu übertragen malloc, da sie zurückgegeben void*werden und a void*auf einen beliebigen Datentyp verweisen kann.

37 Slothworks Oct 10 2015 at 00:47

Dies ist, was das Referenzhandbuch für die GNU C-Bibliothek sagt:

Sie können das Ergebnis von mallocin einer beliebigen Zeigervariablen ohne Umwandlung speichern , da ISO C den Typ void *bei Bedarf automatisch in einen anderen Zeigertyp konvertiert . Die Umwandlung ist jedoch in anderen Kontexten als Zuweisungsoperatoren erforderlich oder wenn Sie möchten, dass Ihr Code in herkömmlichem C ausgeführt wird.

Und tatsächlich sagt der ISO C11-Standard (S. 347) Folgendes:

Der Zeiger, der zurückgegeben wird, wenn die Zuordnung erfolgreich ist, ist in geeigneter Weise ausgerichtet, so dass er einem Zeiger auf einen beliebigen Objekttyp mit einer grundlegenden Ausrichtungsanforderung zugewiesen und dann verwendet werden kann, um auf ein solches Objekt oder ein Array solcher Objekte in dem zugewiesenen Bereich zuzugreifen (bis zum Platz wird explizit freigegeben)

35 Endeavour Jul 13 2014 at 13:42

Ein void-Zeiger ist ein generischer Objektzeiger, und C unterstützt die implizite Konvertierung von einem void-Zeigertyp in andere Typen, sodass keine explizite Typumwandlung erforderlich ist.

Wenn Sie jedoch möchten, dass derselbe Code auf einer C ++ - Plattform, die keine implizite Konvertierung unterstützt, perfekt kompatibel ist, müssen Sie die Typumwandlung durchführen, sodass alles von der Benutzerfreundlichkeit abhängt.

30 swaps Apr 26 2013 at 23:43

Der zurückgegebene Typ ist void *, der in den gewünschten Datenzeigertyp umgewandelt werden kann, um dereferenzierbar zu sein.

29 Jeyamaran Mar 17 2014 at 20:16

Dies hängt von der Programmiersprache und dem Compiler ab. Wenn Sie mallocin C verwenden, müssen Sie cast nicht eingeben, da cast automatisch eingegeben wird. Wenn Sie jedoch C ++ verwenden, sollten Sie cast eingeben, da mallocein void*Typ zurückgegeben wird.

28 AugustKarlstrom Sep 04 2015 at 18:52

In der Sprache C kann jedem Zeiger ein ungültiger Zeiger zugewiesen werden, weshalb Sie keine Typumwandlung verwenden sollten. Wenn Sie eine "typsichere" Zuordnung 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)

Mit diesen können Sie einfach sagen

NEW_ARRAY(sieve, length);

Für nicht dynamische Arrays ist das dritte unverzichtbare Funktionsmakro

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

Das macht Array-Schleifen sicherer und bequemer:

int i, a[100];

for (i = 0; i < LEN(a); i++) {
   ...
}
18 StephenG Dec 05 2015 at 00:27

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

Ich war über die Jahre ziemlich entsetzt über die erstaunlich alten Compiler, die ich verwenden musste. Oft verfolgen Unternehmen und Manager einen äußerst konservativen Ansatz beim Ändern von Compilern und testen nicht einmal , ob ein neuer Compiler (mit besserer Einhaltung von Standards und Codeoptimierung) in ihrem System funktioniert. Die praktische Realität für arbeitende Entwickler ist, dass Sie beim Codieren Ihre Basen abdecken müssen und das Casting von Mallocs leider eine gute Angewohnheit ist, wenn Sie nicht steuern können, welcher Compiler auf Ihren Code angewendet werden kann.

Ich würde auch vorschlagen , dass viele Organisationen einen Codierungsstandard ihrer eigenen Anwendung und das sollten die Methode Menschen sein folgen , wenn sie definiert ist. In Ermangelung einer expliziten Anleitung tendiere ich dazu, am ehesten überall zu kompilieren, anstatt mich sklavisch an einen Standard zu halten.

Das Argument, dass es nach aktuellen Standards nicht notwendig ist, ist durchaus gültig. Dieses Argument lässt jedoch die praktischen Aspekte der realen Welt aus. Wir codieren nicht in einer Welt, die ausschließlich vom Standard des Tages bestimmt wird, sondern von den praktischen Aspekten dessen, was ich gerne als "Realitätsfeld des lokalen Managements" bezeichne. Und das ist mehr verbogen und verdreht als jemals zuvor. :-)

YMMV.

Ich neige dazu, Malloc als Verteidigungsoperation zu betrachten. Nicht schön, nicht perfekt, aber im Allgemeinen sicher. (Ehrlich gesagt, wenn Sie stdlib.h nicht aufgenommen haben, haben Sie weit mehr Probleme als das Casting von Malloc!).

16 Noname Aug 16 2017 at 19:25

Nein, Sie geben das Ergebnis von nicht aus malloc().

Im Allgemeinen gießenvoid * Sie nicht zu oder von .

Ein typischer Grund dafür ist, dass ein Versäumnis #include <stdlib.h>unbemerkt bleiben kann. Dies ist seit langem kein Problem mehr, da C99 implizite Funktionsdeklarationen illegal gemacht hat. Wenn Ihr Compiler also mindestens C99 entspricht, erhalten Sie eine Diagnosemeldung.

Es gibt jedoch einen viel stärkeren Grund, keine unnötigen Zeigerwürfe einzuführen:

In C ist eine Zeigerumwandlung fast immer ein Fehler . Dies liegt an der folgenden Regel ( §6.5 S. 7 in N1570, dem neuesten Entwurf für C11):

Auf einen gespeicherten Wert eines Objekts darf nur über einen lvalue-Ausdruck zugegriffen werden, der einen der folgenden Typen aufweist:
- einen Typ, der mit dem effektiven Typ des Objekts kompatibel ist,
- eine qualifizierte Version eines Typs, der mit dem effektiven Typ des Objekts kompatibel ist,
- ein Typ, der der vorzeichenbehaftete oder vorzeichenlose Typ ist, der dem effektiven Typ des Objekts entspricht,
- ein Typ, der der vorzeichenbehaftete oder vorzeichenlose Typ ist, der einer qualifizierten Version des effektiven Typs des Objekts entspricht,
- ein Aggregat- oder Vereinigungstyp, der einen enthält der oben genannten Typen unter seinen Mitgliedern (einschließlich rekursiv eines Mitglieds eines Unteraggregats oder einer enthaltenen Vereinigung) oder
- eines Zeichentyps.

Dies wird auch als strikte Aliasing-Regel bezeichnet . Der folgende Code ist also undefiniertes Verhalten :

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

Und manchmal überraschend ist auch Folgendes:

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 Sie tun müssen , um gegossene Zeiger, aber die gegebene strenge Aliasing - Regel , müssen Sie damit sehr vorsichtig sein. Jedes Auftreten eines Zeigers in Ihrem Code ist also ein Ort, an dem Sie seine Gültigkeit überprüfen müssen . Daher schreiben Sie niemals eine unnötige Zeigerumwandlung.

tl; dr

Kurz gesagt: Da in C jedes Auftreten einer Zeigerumwandlung eine rote Fahne für Code auslösen sollte, der besondere Aufmerksamkeit erfordert, sollten Sie niemals unnötige Zeigerumwandlungen schreiben .


Randnotizen:

  • Es gibt Fälle, in denen Sie tatsächlich eine Besetzung benötigenvoid * , z. B. wenn Sie einen Zeiger drucken möchten:

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

    Die Besetzung ist hier notwendig, da printf()es sich um eine variable Funktion handelt, sodass implizite Konvertierungen nicht funktionieren.

  • In C ++ ist die Situation anders. Das Umwandeln von Zeigertypen ist beim Umgang mit Objekten abgeleiteter Klassen etwas üblich (und korrekt). Daher macht es Sinn , dass in C ++, die Umwandlung in und aus void *ist nicht implizit. C ++ bietet eine ganze Reihe verschiedener Casting-Varianten.

15 Kaz Mar 30 2016 at 07:23

Ich habe die Besetzung nur eingefügt, um die Missbilligung des hässlichen Lochs im Typsystem zu zeigen, wodurch Code wie das folgende Snippet ohne Diagnose kompiliert werden kann, obwohl keine Besetzungen verwendet werden, um die schlechte Konvertierung herbeizuführen:

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

Ich wünschte, das gäbe es nicht (und es gibt es nicht in C ++) und so habe ich gegossen. Es repräsentiert meinen Geschmack und meine Programmpolitik. Ich werfe nicht nur einen Zeiger, sondern effektiv einen Stimmzettel und treibe Dämonen der Dummheit aus . Wenn ich Dummheit nicht wirklich austreiben kann , dann lassen Sie mich zumindest den Wunsch mit einer Geste des Protests zum Ausdruck bringen.

In der Tat ist es eine gute Praxis, malloc(und Freunde) mit Funktionen zu versehen, die zurückkehren unsigned char *, und im Grunde nie void *in Ihrem Code zu verwenden. Wenn Sie einen generischen Zeiger auf ein beliebiges Objekt benötigen, verwenden Sie ein char *oder unsigned char *und haben Casts in beide Richtungen. Die einzige Entspannung, die man sich vielleicht gönnen kann, ist die Verwendung von Funktionen wie memsetund memcpyohne Abgüsse.

Zum Thema Gießen und C ++ Kompatibilität, wenn Sie Ihren Code schreiben , so dass es kompiliert , da beide C und C ++ (in diesem Fall Sie haben den Rückgabewert zu werfen , mallocwenn es um etwas anderes als die Zuordnung void *), können Sie ein sehr hilfreichen tun Sache für sich selbst: Sie können Makros für das Casting verwenden, die beim Kompilieren als C ++ in Casts im C ++ - Stil übersetzt werden, beim Kompilieren als C ++ jedoch auf einen C-Cast reduziert werden:

/* 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, grepzeigt Ihnen eine einfache Suche in Ihrer Codebasis nach diesen Bezeichnern, wo sich alle Ihre Besetzungen befinden, sodass Sie überprüfen können, ob sie falsch sind.

Wenn Sie dann in Zukunft den Code regelmäßig mit C ++ kompilieren, wird die Verwendung einer geeigneten Besetzung erzwungen. Wenn Sie beispielsweise strip_qualnur ein constoder entfernen volatile, das Programm sich jedoch so ändert, dass jetzt eine Typkonvertierung durchgeführt wird, erhalten Sie eine Diagnose, und Sie müssen eine Kombination von Casts verwenden, um die gewünschte Konvertierung zu erhalten.

Um Ihnen das Festhalten an diesen Makros zu erleichtern, verfügt der GNU C ++ (nicht C!) - Compiler über eine schöne Funktion: eine optionale Diagnose, die für alle Vorkommen von C-Casts erstellt wird.

     -Wold-Style-Cast (nur C ++ und Objective-C ++)
         Warnen Sie, wenn ein Cast im alten Stil (C-Stil) verwendet wird, der nicht leer ist
         innerhalb eines C ++ - Programms. Die neuen Casts (dynamic_cast,
         static_cast, reinterpret_cast und const_cast) sind weniger anfällig
         zu unbeabsichtigten Effekten und viel einfacher zu suchen.

Wenn Ihr C-Code als C ++ kompiliert wird, können Sie mit dieser -Wold-style-castOption alle Vorkommen der (type)Casting-Syntax ermitteln, die sich möglicherweise in den Code eingeschlichen haben, und diese Diagnose weiterverfolgen, indem Sie sie durch eine geeignete Auswahl aus den oben genannten Makros (oder a) ersetzen Kombination, falls erforderlich).

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

15 proski Jun 29 2016 at 14:30

Ich mache die Besetzung lieber, aber nicht manuell. Mein Favorit verwendet g_newund g_new0Makros von glib. Wenn glib nicht verwendet wird, würde ich ähnliche Makros hinzufügen. Diese Makros reduzieren die Codeduplizierung, ohne die Typensicherheit zu beeinträchtigen. Wenn Sie den Typ falsch verstehen, erhalten Sie eine implizite Umwandlung zwischen nicht ungültigen Zeigern, die eine Warnung verursachen würde (Fehler in C ++). Wenn Sie vergessen, den Header einzuschließen, der g_newund definiert g_new0, wird eine Fehlermeldung angezeigt. g_newund g_new0beide nehmen die gleichen Argumente, im Gegensatz dazu mallocweniger Argumente als calloc. Fügen Sie einfach hinzu 0, um den mit Null initialisierten Speicher zu erhalten. Der Code kann ohne Änderungen mit einem C ++ - Compiler kompiliert werden.

14 user877329 Jun 12 2015 at 22:23

Das Beste, was Sie tun können, wenn Sie in C programmieren, wann immer dies möglich ist:

  1. Lassen Sie Ihr Programm über einen C-Compiler mit allen aktivierten Warnungen kompilieren -Wallund beheben Sie alle Fehler und Warnungen
  2. Stellen Sie sicher, dass keine Variablen als deklariert sind auto
  3. Kompilieren Sie es dann mit einem C ++ - Compiler mit -Wallund -std=c++11. Beheben Sie alle Fehler und Warnungen.
  4. Kompilieren Sie nun erneut mit dem C-Compiler. Ihr Programm sollte jetzt ohne Vorwarnung kompiliert werden und weniger Fehler enthalten.

Mit diesem Verfahren können Sie die strikte C ++ - Typprüfung nutzen und so die Anzahl der Fehler reduzieren. Insbesondere zwingt Sie dieses Verfahren zum Einbeziehen stdlib.h, sonst erhalten Sie

malloc wurde nicht in diesem Bereich deklariert

und zwingt Sie auch, das Ergebnis von zu werfen, mallocoder Sie werden erhalten

ungültige Konvertierung von void*nachT*

oder was auch immer Ihr Zieltyp ist.

Die einzigen Vorteile des Schreibens in C anstelle von C ++, die ich finden kann, sind

  1. C hat einen gut spezifizierten ABI
  2. C ++ kann mehr Code generieren [Ausnahmen, RTTI, Vorlagen, Laufzeitpolymorphismus ]

Beachten Sie, dass die zweiten Nachteile im Idealfall verschwinden sollten, wenn Sie die für C gemeinsame Teilmenge zusammen mit dem statischen polymorphen Merkmal verwenden.

Für diejenigen, die strenge C ++ - Regeln als unpraktisch empfinden, können wir die C ++ 11-Funktion mit abgeleitetem Typ verwenden

auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...
12 Noname Sep 10 2015 at 22:58

Casting ist nur für C ++ und nicht für C. Wenn Sie einen C ++ - Compiler verwenden, sollten Sie ihn besser in C-Compiler ändern.

10 Aashish Oct 03 2017 at 20:13

Das Casting von Malloc ist in C nicht erforderlich, in C ++ jedoch obligatorisch.

Casting ist in C nicht erforderlich, weil:

  • void * wird im Fall von C automatisch und sicher auf einen anderen Zeigertyp hochgestuft.
  • Es kann einen Fehler verbergen, wenn Sie vergessen haben, ihn einzuschließen <stdlib.h>. Dies kann zu Abstürzen führen.
  • Wenn Zeiger und Ganzzahlen unterschiedlich groß sind, verbergen Sie eine Warnung durch Umwandeln und verlieren möglicherweise Teile Ihrer zurückgegebenen Adresse.
  • Wenn der Typ des Zeigers bei seiner Deklaration geändert wird, müssen möglicherweise auch alle Zeilen geändert werden, in denen mallocaufgerufen und umgewandelt wird.

Andererseits kann Casting die Portabilität Ihres Programms erhöhen. Das heißt, es ermöglicht einem C-Programm oder einer C-Funktion, als C ++ zu kompilieren.

9 iec2011007 Jul 29 2015 at 12:53

Das Konzept hinter dem Void-Zeiger besteht darin, dass er in jeden Datentyp umgewandelt werden kann, weshalb malloc void zurückgibt. Außerdem müssen Sie sich der automatischen Typumwandlung bewusst sein. Es ist also nicht zwingend erforderlich, den Zeiger umzuwandeln, obwohl Sie dies tun müssen. Es hilft dabei, den Code sauber zu halten und das Debuggen

9 dhanagovindarajan Jul 18 2016 at 17:36

Ein void-Zeiger ist ein generischer Zeiger, und C unterstützt die implizite Konvertierung von einem void-Zeigertyp in andere Typen, sodass keine explizite Typumwandlung erforderlich ist.

Wenn Sie jedoch möchten, dass derselbe Code auf einer C ++ - Plattform, die keine implizite Konvertierung unterstützt, perfekt kompatibel ist, müssen Sie die Typumwandlung durchführen, sodass alles von der Benutzerfreundlichkeit abhängt.

9 Mohit Nov 08 2016 at 17:09
  1. Wie bereits erwähnt, wird es nicht für C, sondern für C ++ benötigt.

  2. Durch das Einschließen der Besetzung kann ein C-Programm oder eine C-Funktion möglicherweise als C ++ kompiliert werden.

  3. In C ist dies nicht erforderlich, da void * automatisch und sicher auf einen anderen Zeigertyp hochgestuft wird.

  4. Wenn Sie dann einen Cast ausführen, kann dies einen Fehler verbergen, wenn Sie vergessen haben, stdlib.h einzuschließen . Dies kann zu Abstürzen führen (oder, schlimmer noch, erst viel später in einem völlig anderen Teil des Codes zu einem Absturz führen).

    Da stdlib.h den Prototyp für malloc enthält, wird gefunden. In Ermangelung eines Prototyps für malloc verlangt der Standard, dass der C-Compiler davon ausgeht, dass malloc ein int zurückgibt. Wenn keine Umwandlung vorliegt, wird eine Warnung ausgegeben, wenn diese Ganzzahl dem Zeiger zugewiesen wird. Bei der Besetzung wird diese Warnung jedoch nicht ausgegeben, wodurch ein Fehler ausgeblendet wird.

4 RobertSsupportsMonicaCellio Jun 13 2020 at 02:20

Diese Frage ist Gegenstand von Meinungsmissbrauch.

Manchmal bemerke ich solche Kommentare:

Wirf nicht das Ergebnis von Malloc

oder

Warum wir nicht das Ergebnis von Malloc wirken

bei Fragen, bei denen OP Casting verwendet. Die Kommentare selbst enthalten einen Hyperlink zu dieser Frage.

Das ist in jeder Hinsicht unangemessen und auch falsch. Es gibt kein Richtig und kein Falsch, wenn es wirklich um den eigenen Codierungsstil geht.


Warum passiert dies?

Es basiert auf zwei Gründen:

  1. Diese Frage ist in der Tat meinungsbasiert. Technisch gesehen hätte die Frage vor Jahren als meinungsbasiert geschlossen werden müssen. Eine " Do I " - oder " Don't I " - oder gleichwertige " Should I " - oder " Shouldn't I " -Frage kann man einfach nicht konzentriert beantworten, ohne die eigene Meinung zu vertreten. Einer der Gründe, eine Frage zu schließen, ist, dass sie "zu meinungsbasierten Antworten führen kann", wie hier gut gezeigt wird.

  2. Viele Antworten (einschließlich der offensichtlichsten und akzeptiertesten Antwort von @unwind ) basieren entweder vollständig oder fast ausschließlich auf Meinungen (z. B. eine mysteriöse "Unordnung", die Ihrem Code hinzugefügt würde, wenn Sie sich selbst besetzen oder wiederholen, wäre schlecht) und zeigen eine klare und konzentrierte Tendenz, die Besetzung wegzulassen. Sie streiten sich über die Redundanz der Besetzung auf einer Seite, aber noch schlimmer darüber, einen Fehler zu lösen, der durch einen Fehler / Fehler in der Programmierung selbst verursacht wird - nicht, #include <stdlib.h>wenn man ihn verwenden möchte malloc().


Ich möchte mit weniger meiner persönlichen Meinung eine zutreffende Sicht auf einige der besprochenen Punkte bringen. Einige Punkte sind besonders zu beachten:

  1. Eine solche sehr anfällige Frage, um in die eigene Meinung zu fallen, bedarf einer Antwort mit neutralen Vor- und Nachteilen. Nicht nur Nachteile oder Vorteile.

    Eine gute Übersicht über Vor- und Nachteile finden Sie in dieser Antwort:

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

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


  1. Ein Grund, der höchstens auftritt, um das Auslassen der Besetzung zu begründen, ist, dass die Besetzung einen Fehler verbergen könnte.

    Wenn jemand ein implizites Deklarat verwendet malloc(), das zurückgibt int(implizite Funktionen sind seit C99 vom Standard entfernt) und sizeof(int) != sizeof(int*), wie in dieser Frage gezeigt

    Warum ist dieser Code in der 64-Bit-Architektur fehlerfrei, funktioniert aber in der 32-Bit-Architektur einwandfrei?

    Die Besetzung würde einen Fehler verbergen.

    Dies ist zwar wahr, zeigt aber nur die Hälfte der Geschichte, da das Weglassen der Besetzung nur eine zukunftsweisende Lösung für einen noch größeren Fehler wäre - ohne Berücksichtigung stdlib.hbei der Verwendung malloc().

    Dies wird niemals ein ernstes Problem sein.

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

    2. Es ist nicht so abwesend, vergessen zu haben stdlib.h, einzuschließen , wenn Sie malloc()in Ihrem Code verwenden möchten , was selbst ein großer Fehler ist.


  1. Einige Leute streiten sich über die C ++ - Konformität von C-Code, da die Besetzung in C ++ vorgeschrieben ist.

    Zunächst einmal 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.

    Wenn Sie jedoch C-Code wirklich C ++ -kompatibel machen möchten / müssen und umgekehrt, verwenden Sie Compiler-Switches anstelle von Cast.

    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:

    • https://stackoverflow.com/a/34094068/12139179

    • https://stackoverflow.com/a/36297486/12139179

    • https://stackoverflow.com/a/33044300/12139179


  1. Die Umwandlung kann nicht vorteilhaft sein, wenn sich Ihr Code bzw. der Typ des zugewiesenen Zeigers (und damit der Typ der Umwandlung) ändert, obwohl dies in den meisten Fällen unwahrscheinlich ist. Dann müssten Sie auch alle Casts warten / ändern, und wenn Sie einige tausend Aufrufe von Speicherverwaltungsfunktionen in Ihrem Code haben, kann dies die Wartungseffizienz wirklich zusammenfassen und verringern.

Zusammenfassung:

Tatsache ist, dass die Umwandlung gemäß dem C-Standard (bereits seit ANSI-C (C89 / C90)) redundant ist, wenn der zugewiesene Zeiger auf ein Objekt mit grundlegender Ausrichtungsanforderung zeigt (das die meisten aller Objekte enthält).

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

"Die Reihenfolge und Kontiguität des Speichers, der durch aufeinanderfolgende Aufrufe der Funktionen align_alloc, calloc, malloc und realloc zugewiesen wird, ist nicht angegeben. Der Zeiger, der bei erfolgreicher Zuweisung zurückgegeben wird, ist geeignet ausgerichtet, sodass er einem Zeiger auf einen beliebigen Objekttyp mit zugewiesen werden kann eine grundlegende Ausrichtungsanforderung und wird dann verwendet, um auf ein solches Objekt oder ein Array solcher Objekte in dem zugewiesenen Raum zuzugreifen (bis der Raum explizit freigegeben wird). "

Quelle: C18, §7.22.3 / 1


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

- alle atomaren, qualifizierten oder nicht qualifizierten Grundtypen;

- alle atomaren, qualifizierten oder nicht qualifizierten Aufzählungstypen;

- alle atomaren, qualifizierten oder nicht qualifizierten Zeigertypen;

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

- alle in Abschnitt 7 als vollständige Objekttypen angegebenen Typen;

- Alle Struktur- oder Vereinigungstypen, deren Elemente alle Typen mit grundlegenden Ausrichtungsanforderungen haben und deren Elemente keinen Ausrichtungsspezifizierer haben, der eine Ausrichtung angibt, die keine grundlegende Ausrichtung ist.

  1. Wie in 6.2.1 angegeben, kann die spätere Deklaration die vorherige Deklaration verbergen. "

Quelle: C18, §6.2.8 / 2

Wenn Sie jedoch Speicher für ein implementierungsdefiniertes Objekt mit erweiterten Ausrichtungsanforderungen zuweisen, wird die Umwandlung benötigt.

Eine erweiterte Ausrichtung wird durch eine Ausrichtung größer als dargestellt _Alignof (max_align_t). Es ist implementierungsdefiniert, ob erweiterte Alignments unterstützt werden und für welche Speicherdauer sie unterstützt werden. Ein Typ mit einer erweiterten Ausrichtungsanforderung ist ein überausgerichteter Typ.58)

Quelle. C18, §6.2.8 / 3

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

Bitte seien Sie vorsichtig, wie Sie sich weiterbilden.

Ich empfehle Ihnen, zuerst alle bisher gemachten Antworten sorgfältig zu lesen (sowie deren Kommentare, die auf einen Fehler hinweisen können) und dann Ihre eigene Meinung zu bilden, wenn Sie oder wenn Sie das Ergebnis nicht malloc()in einem bestimmten Fall abgeben.

Bitte beachten Sie:

Es gibt keine richtige und falsche Antwort auf diese Frage. Es ist eine Frage des Stils und Sie entscheiden selbst, welchen Weg Sie wählen (wenn Sie nicht durch Ausbildung oder Beruf dazu gezwungen werden). Bitte sei dir dessen bewusst und lass dich nicht austricksen .


Letzte Anmerkung: Ich habe kürzlich dafür gestimmt, diese Frage als meinungsbasiert zu schließen, was in der Tat seit Jahren erforderlich ist. Wenn Sie das Privileg zum Schließen / Wiedereröffnen haben, möchte ich Sie auch dazu einladen.

pasignature Mar 05 2020 at 22:56

Für mich ist das Mitnehmen und die Schlussfolgerung hier, dass das Casting mallocin C absolut NICHT notwendig ist, aber wenn Sie jedoch Casting durchführen, hat dies keine Auswirkungen, mallocda mallocIhnen immer noch Ihr gewünschter gesegneter Speicherplatz zugewiesen wird. Ein weiterer Grund zum Mitnehmen ist der Grund oder einer der Gründe, warum Benutzer Casting durchführen. Dadurch können sie dasselbe Programm entweder in C oder C ++ kompilieren.

Es kann andere Gründe geben, aber andere Gründe würden Sie mit ziemlicher Sicherheit früher oder später in ernsthafte Schwierigkeiten bringen.

ivan.ukr May 22 2020 at 14:03

Sie können, müssen aber nicht in C umwandeln. Sie müssen umwandeln, wenn dieser Code als C ++ kompiliert ist.