Warum setzen einige Leute in C den Zeiger um, bevor sie ihn freigeben?

Lesezeit: 11 Minuten

Benutzeravatar von SO Stinks
SO stinkt

Ich arbeite an einer alten Codebasis und so ziemlich jeder Aufruf von free() verwendet eine Umwandlung für sein Argument. Zum Beispiel,

free((float *)velocity);
free((float *)acceleration);
free((char *)label);

wobei jeder Zeiger vom entsprechenden (und übereinstimmenden) Typ ist. Ich sehe überhaupt keinen Sinn darin, dies zu tun. Es ist ein sehr alter Code, also frage ich mich, ob es eine K&R-Sache ist. Wenn ja, möchte ich eigentlich die alten Compiler unterstützen, die dies möglicherweise erfordert haben, also möchte ich sie nicht entfernen.

Gibt es einen technischen Grund, diese Besetzungen zu verwenden? Ich sehe nicht einmal einen pragmatischen Grund, sie zu verwenden. Was bringt es, uns an den Datentyp zu erinnern, bevor wir ihn freigeben?

EDIT: Diese Frage ist nicht ein Duplikat der anderen Frage. Die andere Frage ist ein Sonderfall dieser Frage, was meiner Meinung nach offensichtlich ist, wenn die nahen Wähler alle Antworten gelesen hätten.

Kolophon: Ich setze das Häkchen bei der Antwort, die einen Grund dafür angegeben hat, warum dies möglicherweise noch getan werden muss; Die Antwort, dass es sich um eine Vor-ANSI-C-Benutzerdefinierte handelt (zumindest unter einigen Programmierern), scheint jedoch der Grund zu sein, warum es in meinem Fall verwendet wurde. Wenn es zwei Häkchen zu geben gäbe, würden sie beide eins bekommen. Viele gute Punkte von vielen Leuten hier. Vielen Dank für Ihre Beiträge.

  • “Was bringt es, uns an den Datentyp zu erinnern, bevor wir ihn freigeben?” Vielleicht um zu wissen, wie viel Speicher freigegeben wird?

    – m0skit0

    1. Dezember 2015 um 12:03 Uhr

  • @Codor Der Compiler führt keine Freigabe durch, das Betriebssystem tut dies.

    – m0skit0

    1. Dezember 2015 um 12:04 Uhr

  • @m0skit0 “Vielleicht um zu wissen, wie viel Speicher freigegeben wird?” Der Typ ist nicht erforderlich, um zu wissen, wie viel frei werden soll. Cast nur aus diesem Grund ist schlechte Codierung.

    – Benutzer694733

    1. Dezember 2015 um 12:09 Uhr

  • @m0skit0 Casting aus Gründen der Lesbarkeit ist immer schlechte Codierung, da Casting die Interpretation von Typen ändert und schwerwiegende Fehler verbergen kann. Wenn Lesbarkeit erforderlich ist, sind Kommentare besser.

    – Benutzer694733

    1. Dezember 2015 um 12:14 Uhr

  • In alten Tagen, als Dinosaurier auf der Erde wandelten und Programmierbücher schrieben, gab es meines Erachtens keine void* in Pre-Standard C, aber nur char*. Wenn Ihre archäologischen Funde also Code enthüllen, der den Parameter in free() umwandelt, muss er meiner Meinung nach entweder aus dieser Zeit stammen oder von einer Kreatur aus dieser Zeit geschrieben worden sein. Ich kann jedoch keine Quelle dafür finden, daher verzichte ich auf eine Antwort.

    – Ludin

    1. Dezember 2015 um 12:22 Uhr


Benutzeravatar von Manos Nikolaidis
Manos Nikolaidis

Casting kann erforderlich sein, um Compiler-Warnungen aufzulösen, falls dies der Fall ist const. Hier ist ein Codebeispiel, das eine Warnung auslöst, ohne das Argument „free“ zu übertragen:

const float* velocity = malloc(2*sizeof(float));
free(velocity);

Und der Compiler (gcc 4.8.3) sagt:

main.c: In function ‘main’:
main.c:9:5: warning: passing argument 1 of ‘free’ discards ‘const’ qualifier from pointer target type [enabled by default]
     free(velocity);
     ^
In file included from main.c:2:0:
/usr/include/stdlib.h:482:13: note: expected ‘void *’ but argument is of type ‘const float *’
 extern void free (void *__ptr) __THROW;

Wenn du benutzt free((float*) velocity); der Compiler hört auf zu meckern.

  • @ m0skit0, das erklärt nicht, warum jemand zu casten würde float* vor der Befreiung. Ich habe es versucht free((void *)velocity); mit gcc 4.8.3. Natürlich würde es mit einem alten Compiler nicht funktionieren

    – Manos Nikolaidis

    1. Dezember 2015 um 12:40 Uhr


  • Aber warum sollten Sie konstanten Speicher dynamisch zuweisen? Du könntest es nie gebrauchen!

    – Nils_M

    1. Dezember 2015 um 13:50 Uhr

  • @ Nils_M Es ist ein vereinfachtes Beispiel, um einen Punkt zu machen. Was ich im eigentlichen Code in einer Funktion getan habe, ist, nicht konstanten Speicher zuzuweisen, Werte zuzuweisen, in einen konstanten Zeiger umzuwandeln und ihn zurückzugeben. Jetzt gibt es einen Zeiger auf einen vorab zugewiesenen konstanten Speicher, den jemand freigeben muss.

    – Manos Nikolaidis

    1. Dezember 2015 um 14:19 Uhr

  • Beispiel: „Diese Subroutinen geben den String im neu mallocierten Speicher zurück, auf den *stringValueP zeigt, den Sie schließlich freigeben müssen. Manchmal wird die Betriebssystemfunktion, die Sie zum Freigeben von Speicher verwenden, so deklariert, dass sie einen Zeiger auf etwas Nicht-Konstantes als Argument verwendet, weil *stringValueP ein Zeiger auf eine Konstante ist.

    – Carsten S

    1. Dezember 2015 um 22:20 Uhr

  • Fehlerhaft, wenn eine Funktion dauert const char *p als Argument verwendet und es dann freigibt, ist es richtig, nicht zu wirken p zu char* bevor Sie kostenlos anrufen. Es ist, es nicht als Einnahme zu deklarieren const char *p in erster Linie, da es modifiziert *p und sind entsprechend zu deklarieren. (Und wenn es einen const-Zeiger anstelle eines Zeigers auf const braucht, int *const pdu nicht brauchen zu casten, da es eigentlich legal ist und somit auch ohne Cast funktioniert.)

    – Strahl

    3. Dezember 2015 um 1:06 Uhr


Benutzeravatar von Lundin
Lundin

Pre-Standard C hatte keine void* aber nur char*, also mussten Sie alle übergebenen Parameter umwandeln. Wenn Sie auf alten C-Code stoßen, finden Sie daher möglicherweise solche Umwandlungen.

Ähnliche Frage mit Referenzen.

Als der erste C-Standard veröffentlicht wurde, änderten sich die Prototypen für malloc und free von have char* zum void* die sie heute noch haben.

Und natürlich sind solche Casts in Standard-C überflüssig und beeinträchtigen nur die Lesbarkeit.

  • Aber warum sollten Sie das Argument aufwerfen free auf die gleiche Art, die es bereits ist?

    – jwodder

    1. Dezember 2015 um 13:49 Uhr

  • @chux Das Problem mit der Vornorm ist genau das: Es gibt keine Verpflichtungen für irgendetwas. Die Leute zeigten nur auf das K&R-Buch für Canon, weil das das einzige war, was sie hatten. Und wie wir an mehreren Beispielen in K&R 2nd Edition sehen können, ist K&R selbst verwirrt darüber, wie der Parameter umgewandelt wird free Arbeiten Sie in Standard-C (Sie müssen nicht umwandeln). Ich habe die 1. Ausgabe nicht gelesen, daher kann ich nicht sagen, ob sie auch in den 80er Jahren vor dem Standard verwirrt waren.

    – Ludin

    1. Dezember 2015 um 15:38 Uhr


  • Pre-Standard C hatte nicht void*aber es gab auch keine Funktionsprototypen, also wurde das Argument von gecastet free war auch in K&R noch unnötig (vorausgesetzt, alle Datenzeigertypen verwendeten die gleiche Darstellung).

    – Ian Abbott

    1. Dezember 2015 um 16:04 Uhr


  • Aus mehreren Gründen, die bereits in den Kommentaren angegeben wurden, halte ich diese Antwort nicht für sinnvoll.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    1. Dezember 2015 um 17:07 Uhr

  • Ich sehe nicht, wie diese Antwort wirklich etwas Relevantes beantworten würde. Die ursprüngliche Frage betrifft Umwandlungen in andere Typen, nicht nur in char *. Welchen Sinn würde es in alten Compilern ohne machen void? Was würden solche Besetzungen bewirken?

    – AnT steht zu Russland

    1. Dezember 2015 um 20:56 Uhr

Benutzeravatar von egur
z.B

Hier ist ein Beispiel, bei dem free ohne Cast fehlschlagen würde:

volatile int* p = (volatile int*)malloc(5 * sizeof(int));
free(p);        // fail: warning C4090: 'function' : different 'volatile' qualifiers
free((int*)p);  // success :)
free((void*)p); // success :)

In C können Sie eine Warnung erhalten (haben Sie eine in VS2012). In C++ erhalten Sie einen Fehler.

Abgesehen von seltenen Fällen bläht das Casting den Code nur auf …

Bearbeiten:
Ich habe gecastet void* nicht int* um den Fehler zu demonstrieren. Es wird genauso funktionieren wie int* wird umgewandelt in void* implizit. Hinzugefügt int* Code.

  • Beachten Sie, dass in dem in der Frage geposteten Code die Casts nicht zu verwenden sind void *aber zu float * und char *. Diese Besetzungen sind nicht nur nebensächlich, sie sind falsch.

    – Andreas Henle

    1. Dezember 2015 um 12:18 Uhr

  • Die Frage zielt eigentlich auf das Gegenteil.

    – m0skit0

    1. Dezember 2015 um 12:18 Uhr


  • Ich verstehe die Antwort nicht; in welchem ​​Sinne würde free(p) scheitern? Würde es einen Compiler-Fehler geben?

    – Codor

    1. Dezember 2015 um 12:35 Uhr


  • Das sind gute Punkte. Gleiches gilt für const Qualifizierungszeiger, offensichtlich.

    – Ludin

    1. Dezember 2015 um 12:58 Uhr

  • volatile existiert, seit C standardisiert wurde, wenn nicht länger. Es war nicht in C99 hinzugefügt.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    1. Dezember 2015 um 17:08 Uhr

chux – Stellt Monicas Benutzeravatar wieder her
Chux – Wiedereinsetzung von Monica

Alter Grund: 1. Durch die Verwendung free((sometype*) ptr)gibt der Code explizit den Typ an, der als Teil des Zeigers betrachtet werden soll free() Anruf. Die explizite Umwandlung ist nützlich, wenn free() wird durch ein (Do-it-yourself) ersetzt DIY_free().

#define free(ptr) DIY_free(ptr, sizeof (*ptr))

EIN DIY_free() war (ist) eine Möglichkeit, insbesondere im Debug-Modus, eine Laufzeitanalyse des freigegebenen Zeigers durchzuführen. Dies wird oft mit a gepaart DIY_malloc() um Sätze, globale Speichernutzungszählungen usw. hinzuzufügen. Meine Gruppe verwendete diese Technik jahrelang, bevor modernere Tools auftauchten. Voraussetzung dafür ist, dass der zu befreiende Gegenstand nach der Art gegossen wurde, der er ursprünglich zugeteilt war.

  1. Angesichts der vielen Stunden, die mit der Suche nach Speicherproblemen usw. verbracht wurden, würden kleine Tricks wie das Casten des Typs free’d beim Suchen und Eingrenzen des Debugging helfen.

Modern: Vermeiden const und volatile Warnungen wie von Manos Nikolaidis@ und @egur angesprochen. Ich dachte, ich würde die Auswirkungen der 3 beachten Qualifikanten: const, volatileund restrict.

[edit] Hinzugefügt char * restrict *rp2 per @R.. Kommentar

void free_test(const char *cp, volatile char *vp, char * restrict rp, 
    char * restrict *rp2) {
  free(cp);  // warning
  free(vp);  // warning
  free(rp);  // OK
  free(rp2);  // warning
}

int main(void) {
  free_test(0,0,0,0);
  return 0;
}

Benutzeravatar von twol
zwöl

Hier ist eine weitere Alternativhypothese.

Uns wurde gesagt, dass das Programm vor C89 geschrieben wurde, was bedeutet, dass es keine Art von Nichtübereinstimmung mit dem Prototyp von umgehen kann freeweil es nicht nur so etwas wie nicht gab const Noch void * Vor C89 gab es so etwas wie a nicht Funktionsprototyp vor C89. stdlib.h selbst war eine Erfindung des Komitees. Wenn sich die Systemheader die Mühe gemacht haben, zu deklarieren free überhaupt hätten sie es so gemacht:

extern free();  /* no `void` return type either! */

Nun, der entscheidende Punkt hier ist, dass das Fehlen von Funktionsprototypen bedeutete, dass der Compiler dies tat keine Überprüfung des Argumenttyps. Es wendete die Standard-Argument-Promotions an (die gleichen, die immer noch für variadische Funktionsaufrufe gelten) und das war es. Die Verantwortung dafür, dass die Argumente an jeder Callsite mit den Erwartungen des Angerufenen in Einklang gebracht werden, liegt bei ihm völlig mit dem Programmierer.

Dies bedeutet jedoch noch nicht, dass es notwendig war, das Argument zu werfen free auf den meisten K&R-Compilern. Eine Funktion wie

free_stuff(a, b, c)
    float *a;
    char *b;
    int *c;
{
    free(a);
    free(b);
    free(c);
}

sollte korrekt kompiliert sein. Ich denke, was wir hier haben, ist ein Programm, das geschrieben wurde, um mit einem fehlerhaften Compiler für eine ungewöhnliche Umgebung fertig zu werden: zum Beispiel eine Umgebung, in der sizeof(float *) > sizeof(int) und der Compiler würde nicht Verwenden Sie die entsprechende Aufrufkonvention für Zeiger, es sei denn, Sie setzen sie am Punkt des Aufrufs um.

Mir ist keine solche Umgebung bekannt, aber das bedeutet nicht, dass es keine gab. Die wahrscheinlichsten Kandidaten, die mir in den Sinn kommen, sind abgespeckte “Tiny C”-Compiler für 8- und 16-Bit-Mikros in den frühen 1980er Jahren. Ich wäre auch nicht überrascht zu erfahren, dass frühe Crays solche Probleme hatten.

  • Der ersten Hälfte stimme ich voll und ganz zu. Und die zweite Hälfte ist eine faszinierende und plausible Vermutung.

    – chux – Wiedereinsetzung von Monica

    3. Dezember 2015 um 3:59 Uhr


  • „Mir ist keine solche Umgebung bekannt, aber das bedeutet nicht, dass es keine gab.“ – x86_64 ist einer, wo sizeof(float *) > sizeof(int) obwohl ich nicht glaube, dass es viele K&R-Compiler/Code für x86_64 gibt.

    – Maciej Piechotka

    28. Februar 2021 um 8:55 Uhr

  • Ein größeres Problem wären Plattformen, auf denen Zeiger auf verschiedene Typen unterschiedliche Größen oder Darstellungen haben, z. B. die Darstellung von an int* Verwenden einer Wortadresse, die in einem Wort gehalten wird, aber a darstellt char* mit zwei Wörtern. Code, der bestanden wurde free() ein Zeiger, der nicht darstellungskompatibel war void* oder char* würde einen Guss oder Prototyp benötigen, um richtig zu funktionieren.

    – Superkatze

    4. Januar um 23:45 Uhr

Benutzeravatar der Community
Gemeinschaft

free akzeptiert nur nicht-konstante Zeiger als Parameter. Im Fall von konstanten Zeigern ist daher eine explizite Umwandlung in einen nicht konstanten Zeiger erforderlich.

Konstante Zeiger in C können nicht freigegeben werden

  • Der ersten Hälfte stimme ich voll und ganz zu. Und die zweite Hälfte ist eine faszinierende und plausible Vermutung.

    – chux – Wiedereinsetzung von Monica

    3. Dezember 2015 um 3:59 Uhr


  • „Mir ist keine solche Umgebung bekannt, aber das bedeutet nicht, dass es keine gab.“ – x86_64 ist einer, wo sizeof(float *) > sizeof(int) obwohl ich nicht glaube, dass es viele K&R-Compiler/Code für x86_64 gibt.

    – Maciej Piechotka

    28. Februar 2021 um 8:55 Uhr

  • Ein größeres Problem wären Plattformen, auf denen Zeiger auf verschiedene Typen unterschiedliche Größen oder Darstellungen haben, z. B. die Darstellung von an int* Verwenden einer Wortadresse, die in einem Wort gehalten wird, aber a darstellt char* mit zwei Wörtern. Code, der bestanden wurde free() ein Zeiger, der nicht darstellungskompatibel war void* oder char* würde einen Guss oder Prototyp benötigen, um richtig zu funktionieren.

    – Superkatze

    4. Januar um 23:45 Uhr

1425020cookie-checkWarum setzen einige Leute in C den Zeiger um, bevor sie ihn freigeben?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy