Werfe ich das Ergebnis von Malloc?


2408

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?


222
Es ist auch wartbarer, Sieb = malloc (Größe von * Sieb * Länge) zu schreiben;
William Pursell


3
Die Antworten hier sind ein Minenfeld der Einseitigkeit. Die Antwort lautet "es kommt darauf an". Es ist nicht notwendig, dass das Programm funktioniert. Einige Codierungsstandards erfordern dies jedoch ... Siehe beispielsweise CERT C-Codierungsstandard
Dr. Person Person II

Seltsamerweise sind sich alle einig, dass Sie nicht besetzen NULL. (Das ist wahrscheinlich der Grund, warum C ++ eingeführt wurde nullptr: C ++ erlaubt keine impliziten Zeigerumwandlungen)
Sapphire_Brick

Sie können, aber Sie müssen nicht. Aber Sie müssen in C ++.
Asadefa

Antworten:


2217

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. Dies hat einfach keine Vorteile, aber eine Reihe potenzieller Risiken und die Einbeziehung der Besetzung weisen darauf hin, dass Sie die Risiken nicht kennen.

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 weiter 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.


21
Bitte erwägen Sie, die Antwort zu aktualisieren. Die Besetzung ist nicht länger gefährlich und es ist nicht unbedingt eine schlechte Sache, sich zu wiederholen (Redundanz kann helfen, Fehler zu erkennen).
n. 'Pronomen' m.

11
Compiler haben sich geändert. Ein aktueller Compiler warnt Sie vor einer fehlenden Malloc-Deklaration.
n. 'Pronomen' m.

55
@nm Ok. Ich denke, es ist schlecht anzunehmen, dass jeder, der hier liest, einen bestimmten Compiler hat. Da C11 das gesamte Konzept der "impliziten Funktion" weg ist, wusste ich das auch nicht. Trotzdem sehe ich keinen Sinn darin, eine sinnlose Besetzung hinzuzufügen. Tun Sie das auch int x = (int) 12;nur, um die Dinge klar zu machen?
Entspannen Sie

22
@nm Wenn das explizite Wirken eines ungültigen Zeigers zur Behebung eines Fehlers "beigetragen" hat, ist es wahrscheinlicher, dass Sie auf undefiniertes Verhalten gestoßen sind. Dies würde bedeuten, dass das betreffende Programm wahrscheinlich einen weitaus schlimmeren, unentdeckten Fehler aufweist, auf den Sie noch nicht gestoßen sind. Und eines Tages, an einem kalten Winterabend, werden Sie von der Arbeit nach Hause kommen und feststellen, dass Ihre GitHub-Seite mit Problemberichten überflutet ist, die sich über Dämonen beschweren, die aus den Nasen der Benutzer fliegen
Braden Best

12
@unwind Auch ich stimme dir zu, (int)12ist nicht vergleichbar. 12 ist eine int, die Besetzung macht einfach nichts. Das Retval von malloc()ist void *nicht der Zeigertyp , in den gegossen wurde. (Wenn es nicht ist void *. Also wäre die Analogie zu dem (int)12, (void*)malloc(…)worüber niemand spricht.)
Amin Negm-Awad

376

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 befreit Sie außerdem davon, dass Sie sich Sorgen machen müssen, 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.


71
@MAKZ Ich würde argumentieren, malloc(length * sizeof *sieve)dass es so aussieht, als wäre sizeofes eine Variable - also denke ich, dass malloc(length * sizeof(*sieve))es besser lesbar ist.
Michael Anderson

21
Und malloc(length * (sizeof *sieve))noch lesbarer. MEINER BESCHEIDENEN MEINUNG NACH.
Toby Speight

19
@ Michael Anderson ()Problem beiseite, beachten Sie, dass Ihre vorgeschlagene Art der Bestellung eingeschaltet. Wichtige Überlegungen bei Elementzählwert wie berechnet wird length*width, die halten sizeofwird zuerst in diesem Fall versichert die Multiplikation mit mindestens getan size_tmath. Vergleiche malloc(sizeof( *ptr) * length * width)vs. malloc(length * width * sizeof (*ptr))- der 2. kann überlaufen, length*widthwenn width,lengthkleinere Typen das sind size_t.
chux

3
@chux es ist nicht offensichtlich, aber die Antwort wurde so bearbeitet, dass mein Kommentar weniger relevant ist - der ursprüngliche Vorschlag warmalloc(sizeof *sieve * length)
Michael Anderson

12
C ist nicht C ++. Das Vorgeben, dass sie es sind, wird letztendlich zu Verwirrung und Traurigkeit führen. Wenn Sie C ++ verwenden, ist eine Besetzung im C-Stil ebenfalls schlecht (es sei denn, Sie verwenden einen sehr alten C ++ - Compiler). Und static_cast>()(oder reinterpret_cast<>()) ist mit keinem Dialekt von C. kompatibel
David C.

349

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 dasselbe 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()Bugs 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 eine Behauptung, 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.

39
@ulidtko Falls Sie es nicht wussten, können Sie Code schreiben, der sowohl als C als auch als C ++ kompiliert wird. Tatsächlich sind die meisten Header-Dateien so und enthalten häufig Code (Makros und Inline-Funktionen). Eine .c/ .cpp-Datei zum Kompilieren als beides zu haben, ist nicht sehr oft nützlich, aber ein Fall ist das Hinzufügen von C ++ - throwUnterstützung beim Kompilieren mit dem C ++ - Compiler (aber return -1;beim Kompilieren mit dem C-Compiler oder was auch immer).
Hyde

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

15
Nun, Punkt 1 ist irrelevant, da C! = C ++, die anderen Punkte ebenfalls trivial sind, wenn Sie die Variable in Ihrem mallocAufruf verwenden: char **foo = malloc(3*sizeof(*foo));wenn ganz sicher: 3 Zeiger auf Zeichenzeiger. dann Schleife und tun foo[i] = calloc(101, sizeof(*(foo[i])));. Ordnen Sie ein Array mit 101 Zeichen zu, das sauber auf Nullen initialisiert ist. Keine Besetzung erforderlich. Ändern Sie die Erklärung in unsigned charoder einen anderen Typ, und Sie sind immer noch gut
Elias Van Ootegem

34
Wenn ich gelernt habe, dass ich es habe, kommt es! Fantastische Antwort. Es ist das erste Mal hier in StackOverflow, dass ich zwei entgegengesetzte Antworten +1 habe! +1 Nein, du wirfst nicht und +1 Ja, du wirfst! LOL. Ihr seid großartig. Und für mich und meine Schüler habe ich mich entschieden: Ich besetze. Die Art der Fehler, die die Schüler machen, wird beim Casting leichter erkannt.
Dr. Beco

15
@Leushenko: Es ist schlecht, sich auf eine Weise zu wiederholen, die weder maschinell noch vor Ort überprüft werden kann. Sich auf eine Weise zu wiederholen, die auf diese Weise validiert werden kann, ist weniger schlecht. Angesichts struct Zebra *p; ... p=malloc(sizeof struct Zebra);dessen kann das Malloc nicht vermeiden, Informationen über den Typ von p zu duplizieren, aber weder der Compiler noch die Überprüfung des lokalen Codes würden ein Problem erkennen, wenn sich ein Typ ändert, der andere jedoch nicht. Ändern Sie den Code in p=(struct Zebra*)malloc(sizeof struct Zebra);und der Compiler wird kreischen, wenn der Besetzungstyp nicht übereinstimmt p, und die lokale Inspektion wird zeigen ...
Supercat

170

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.


17
Da Sie ohnehin ein Makro verwenden, warum verwenden Sie es nicht newin der Definition von C ++?
Hosam Aly

63
Weil es keinen Grund dafür gibt. Es ist hauptsächlich für C-Programme gedacht, die mit einem C ++ - Compiler kompiliert wurden. Wenn Sie "neu" verwenden, erhalten Sie nur Probleme. Sie benötigen dann auch ein Makro kostenlos. Und Sie benötigen ein Makro, um ein Array
freizugeben

8
Ganz zu schweigen davon, dass nicht Sie den Speicher freigeben, sondern möglicherweise eine von Ihnen verwendete C-Bibliothek usw. Viele mögliche Probleme ohne Gewinn.
Quinmars

86
@ Hosam: Ja, das ist es definitiv. Wenn Sie verwenden new, müssen Sie verwenden, deleteund wenn Sie verwenden malloc(), müssen Sie verwenden free(). Mischen Sie sie niemals.
Graeme Perrow

17
Wenn man diesen Ansatz wählt, NEWist das Aufrufen des Makros wahrscheinlich eine schlechte Idee, da die Ressource niemals mit delete(oder DELETE) zurückgegeben wird, sodass Sie Ihren Wortschatz mischen. Stattdessen wäre es sinnvoller , es zu benennen MALLOC, oder besser gesagt CALLOCin diesem Fall.
Mah

139

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, wird weiterhin eine Warnung angezeigt. 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.


1
Was bedeutet " Casting kann dem Entwickler helfen, Inkonsistenzen bei der Typgröße zu identifizieren, falls sich der Zielzeigertyp ändert, insbesondere wenn der Zeiger weit vom malloc()Aufruf entfernt deklariert ist "? Könnten Sie ein Beispiel geben?
Spikatrix

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

7
Dies ist die richtige Antwort: Es gibt Vor- und Nachteile, und es kommt auf den Geschmack an (es sei denn, der Code muss als C ++ kompiliert werden - dann ist die Besetzung obligatorisch).
Peter - Monica

3
Punkt 3 ist umstritten, da Wenn der Typ des Zeigers bei seiner Deklaration geändert wird, sollte jede Instanz von malloc, realloc und freiem Auflösen dieses Typs überprüft werden. Das Casting wird dich dazu zwingen, genau das zu tun.
Michaël Roy

104

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.


100

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

Der häufigste Grund, warum Menschen das Ergebnis von Malloc wirken, ist, 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. Wenn Sie jedoch in C ++ programmieren, sollten Sie newmalloc () verwenden und nicht. 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 explizite Casting des Ergebnisses von malloc 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?


9
"sinnlose Unordnung" ist eine abweisende Übertreibung, die dazu neigt, jede Möglichkeit zu entgleisen, jemanden zu überzeugen, der Ihnen noch nicht zustimmt. Eine Besetzung ist sicherlich nicht sinnlos; Die Antworten von Ron Burk und Kaz sprechen sich für ein Casting aus, dem ich sehr zustimme. Ob diese Bedenken mehr wiegen als die von Ihnen erwähnten Bedenken, ist eine vernünftige Frage. Für mich sind Ihre Bedenken im Vergleich zu ihren relativ gering.
Don Hatch

"Ein ungültiger Zeiger kann ohne explizite Umwandlung in einen anderen Zeigertyp konvertiert werden" wird von 6.3.2.3 nicht unterstützt. Vielleicht denken Sie an "Zeiger auf einen Objekttyp"? "void pointer" und "pointer to a function" sind nicht so leicht konvertierbar.
chux

In der Tat war die Referenz unvollständig. Der relevante Teil für die "implizite Aussage" ist die Regel der einfachen Zuordnung 6.5.16.1. "Ein Operand ist ein Zeiger auf einen Objekttyp und der andere ist ein Zeiger auf eine qualifizierte oder nicht qualifizierte Version von void". Ich habe diesen Verweis der Vollständigkeit halber zur Antwort hinzugefügt.
Lundin

91

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


6
@Jens: OK, vielleicht ist der korrektere Wortlaut "implizite Konvertierung". Wie die Verwendung einer integralen Variablen im Gleitkommaausdruck.
EFraim

@EFraim Das würde tatsächlich zu einer Besetzung führen, und zwar zu einer impliziten.
Mad Physicist

71

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


2
Compiler-Warnungen zu schließen ist eine schlechte Idee.
Albert van der Horst

8
@AlbertvanderHorst Nicht, wenn Sie das genaue Problem lösen, ist die Warnung da, um Sie zu warnen.
Dan Bechard

@ Dan. Wenn mit der Lösung des genauen Problems ein Umschreiben einer Unterroutine gemeint ist, um moderne ANSI C-Typen anstelle von char * zurückzugeben, stimme ich zu. Ich würde das nicht als das Herunterfahren des Compilers bezeichnen. Geben Sie Managern nicht nach, die darauf bestehen, dass keine Compiler-Warnungen vorhanden sind, anstatt sie bei jeder Neukompilierung zu verwenden, um mögliche Probleme zu finden. Groetjes Albert
Albert van der Horst

53

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 war früher nur absolut spezifisch und brachte die Schüler in die Mentalität, 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. Daher hat es keine Auswirkung, wenn Sie die Zeigernotation verwenden. In der Array-Notation wird dies erforderlich.


3
Sofern nicht bereits erwähnt, kann die Besetzung Fehler verbergen und die Analyse des Codes für den Compiler oder den statischen Analysator erschweren.
Lundin

2
"Im Wesentlichen ändert das Casting nichts an der Funktionsweise". Das Casting auf den passenden Typ sollte nichts ändern, aber sollte sich der Typ des Vars ändern und der Cast nicht mehr übereinstimmen, könnten Probleme auftreten? IWOs, der Cast- und der Var-Typ sollten synchron gehalten werden - doppelt so viele Wartungsarbeiten.
chux

Ich kann sehen, warum Profis das Casting bevorzugen. Das Casting kann unter pädagogischen Gesichtspunkten nützlich sein, wenn es dem Ausbilder Informationen übermittelt und der Schülercode nicht gepflegt werden muss - sein Wegwerfcode. Aus Sicht der Codierung, Peer-Review und Wartungp = malloc(sizeof *p * n); ist dies jedoch so einfach und besser.
chux

53

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.


34

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.


2
Es ist kein normaler Anwendungsfall, eine einzelne Quelle sowohl als C als auch als C ++ zu kompilieren (im Gegensatz beispielsweise zur Verwendung einer Header-Datei mit Deklarationen zum Verknüpfen von C- und C ++ - Code). Die Verwendung von mallocund Freunden in C ++ ist ein gutes Warnsignal dafür, dass es besondere Aufmerksamkeit verdient (oder in C neu geschrieben wird).
Toby Speight

1
"Ein ungültiger Zeiger ist ein generischer Zeiger" -> "Ein ungültiger Zeiger ist ein generischer Objektzeiger ". Die Größe der Funktionszeiger kann überschritten werden void *und reicht daher void *nicht aus, um einen Funktionszeiger gut zu speichern.
chux

Meine Absicht dieser Zeile war dieselbe, aber trotzdem danke ich @chux für den Vorschlag.
Bemühen Sie sich

33

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

Sie können das Ergebnis von mallocin jeder 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 geeignet 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)


31

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


1
void* kann auf den gewünschten Typ umgewandelt werden, dies ist jedoch nicht erforderlich, da es automatisch konvertiert wird. Die Besetzung ist also nicht notwendig und aus den in den Antworten mit hoher Punktzahl genannten Gründen in der Tat unerwünscht.
Toby Speight

Aber nur, wenn Sie es "on the fly" dereferenzieren müssen. Wenn Sie stattdessen eine Variable erstellen, wird sie sicher und automatisch in den effektiven Typ der Variablen konvertiert, ohne sie zu wirken (in C).
Ferrarezi

28

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++) {
   ...
}

"Ein Objektzeiger kann jedem Objektzeiger zugewiesen werden " Funktionszeiger sind ein weiteres Problem, wenn auch kein Problem malloc().
Chux

Das Zuweisen eines void*zu / von einem Funktionszeiger kann Informationen verlieren, so dass "in jedem Fall ein ungültiger Zeiger einem beliebigen Zeiger zugewiesen werden kann" ein Problem darstellt. Das Zuweisen eines void*, von malloc() zu einem beliebigen Objektzeiger ist jedoch kein Problem.
chux

Der doSchleifenkommentar bezog sich auf Makros, die eine Schleife beinhalten, die sich jedoch von der Titelfrage entfernt. Diesen Kommentar entfernen. Werde diesen auch später runter nehmen.
chux

27

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.


1
Die Funktion malloc gibt auch in C einen ungültigen Zeiger zurück, aber die Regeln der Sprache unterscheiden sich von C ++.
August Karlstrom

16

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!).


15

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 Programmierpolitik. 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 reduzieren:

/* 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 bei der Einhaltung dieser Makros zu helfen, 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.


Wie bereits erwähnt, würde ich normalerweise empfehlen, C- und C ++ - Code nicht zu mischen. Wenn Sie jedoch gute Gründe dafür haben, können Makros hilfreich sein.
Phil1970

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

Das heißt, ich habe im vorherigen Kommentar versucht zu sagen, dass es keine Mischung von C und C ++ gibt. Die Absicht ist, dass der Code alle als C oder alle als C ++ kompiliert wird.
Kaz

15

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...

18
Verwenden Sie einen C-Compiler für C-Code. Verwenden Sie einen C ++ - Compiler für C ++ - Code. Kein Wenn, kein Aber. Das Umschreiben Ihres C-Codes in C ++ ist eine ganz andere Sache und kann die Zeit und die Risiken wert sein - oder auch nicht.
Toby Speight

2
Ich mag @TobySpeight Rat hinzuzufügen: Wenn Sie C - Code in einem C ++ Projekt verwenden müssen, können Sie in der Regel den C - Code als C (zB kompiliert gcc -c c_code.c), die C ++ Code als C ++ (zB g++ -c cpp_code.cpp), und dann verbinden sie zusammen (zB gcc c_code.o cpp_code.ooder umgekehrt, abhängig von den Projektabhängigkeiten). Jetzt sollte es keinen Grund geben, sich jeglicher netter Merkmale beider Sprachen zu berauben ...
autistisch

1
@ user877329 Es ist eine sinnvollere Alternative zum sorgfältigen Hinzufügen von Casts zum Code, die die Lesbarkeit des Codes beeinträchtigen, nur um "C ++ - kompatibel" zu sein.
autistische

1
Wahrscheinlich besteht der Hauptvorteil in diesem Zusammenhang darin, dass Sie mit C schreiben können p = malloc(sizeof(*p));, was bei pÄnderungen an einem anderen Typnamen überhaupt nicht geändert werden muss. Der vorgeschlagene "Vorteil" des Castings besteht darin, dass Sie einen Kompilierungsfehler erhalten, wenn pder falsche Typ vorliegt , aber es ist noch besser, wenn er einfach funktioniert.
Peter Cordes

1
Ich möchte erwähnen, dass das Schreiben in C erforderlich sein kann, wenn auf Plattformen mit geeigneten C ++ - Compilern abgezielt wird. Ausnahmen und Vorlagen sind Funktionen, die C ++ normalerweise dabei helfen, kleineren und / oder effizienteren Code zu generieren, während der Laufzeitpolymorphismus in C ++ größtenteils C entspricht.
user7860670

15

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 den 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 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.


1
In Ihren Beispielen vermeiden Sie void *. Es gibt einen Unterschied zwischen der Besetzung von double * nach int * und umgekehrt. malloc gibt pointel zurück, das auf den größten Standardtyp ausgerichtet ist, sodass keine Aliasing-Regeln verletzt werden, selbst wenn jemand diesen ausgerichteten Zeiger auf einen anderen Typ umwandelt.
P__J__

Aliasing hat überhaupt nichts mit Ausrichtung und für den Rest Ihres Kommentars zu tun - Sie haben offensichtlich nicht verstanden, worum es geht.

@PeterJ: Nur für den Fall, es geht darum , unnötige Zeigerumwandlungen zu vermeiden , damit es nicht wie ein Stück Code aussieht, dem Sie besondere Aufmerksamkeit schenken müssen.

Das strikte Aliasing-Problem hat eigentlich nichts mit ungültigen Zeigern zu tun. Um Fehler zu erhalten, die durch strikte Aliasing-Verstöße verursacht werden, müssen Sie die Referenzdaten deaktivieren. Und da Sie einen ungültigen Zeiger nicht de-referenzieren können, beziehen sich solche Fehler per Definition nicht auf den ungültigen Zeiger, sondern auf etwas anderes.
Lundin

Vielmehr müssten Sie eine Regel festlegen, um alle Zeiger-Casts zu verbieten. Aber wie würden Sie dann Dinge wie Serialisierungsroutinen und hardwarebezogene Programmierung schreiben? Dinge, die Cs Stärke sind. Solche Casts sind in Ordnung, wenn Sie wissen, was Sie tun.
Lundin

15

Ich mache die Besetzung lieber, aber nicht manuell. Mein Favorit ist die Verwendung von 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.


12

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.


9

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


11
" Es ist nicht obligatorisch - obwohl du es tun musst " - ich denke, da gibt es einen Widerspruch!
Toby Speight

5
Ich denke, Sie sollten diesen Beitrag jemandem vorlesen und sehen, ob er versteht, was Sie sagen wollen. Schreiben Sie es dann neu und machen Sie klar, was Sie sagen möchten. Ich kann wirklich nicht verstehen, was Ihre Antwort ist.
Bill Woodger

9

Ein ungültiger Zeiger ist ein generischer Zeiger, und C unterstützt die implizite Konvertierung von einem ungültigen 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
  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.


7

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.


0

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 mag andere Gründe geben, aber andere Gründe würden Sie mit ziemlicher Sicherheit früher oder später in ernsthafte Schwierigkeiten bringen.


0

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.