Allozierende Funktionen kennzeichnen
-
Hallo zusammen,
ich habe mal eine Frage zur optimalen Herangehensweise.
Als Beispiel: Ich habe eine Funktion, die auf Zeichenketten arbeitet (etwa ein XML-Parser), dieser soll nun irgendwie eine Teilzeichenkette zurückliefern.
Dafür gäbe es ja zwei Möglichkeiten:
1. Ich alloziere mir vorm Funktionsaufruf Speicher, in den die Funktion hineinschreibt -> Problem: Es ist ja noch nicht bekannt, wie groß der Speicherbedarf ist, da dieser erst von der Funktion berechnet werden muss. Und mir Speicher im KB-Bereich zu reservieren, damit es auf jeden Fall passt, erscheint mir doch etwas unperformant.2. Ich alloziere in der Funktion den exakten Speicherplatz und gebe einen Pointer auf diesen zurück. -> Problem: Woher weiss der Aufrufende (ohne Kenntnis meiner Implementierung), dass ich aktiv alloziert habe und er anschließend irgendwann free() rufen muss? Gibt es da irgendwelche Variablendekoratoren oder ähnliches?
Und wie würdet ihr das Problem lösen?
Gruß,
Daniel
-
Die Funktion koennte doch z.B. ein int-Wert zurueckliefern, der dann von der aufrufenden Funktion ausgewertet wird:
int funktion() { ... if(alloziert) return 1; else return 0; }
-
In C brauchst du bei 2. Dokumentation und Disziplin.
Irgendwo (z.B. in der .h Dateien) sollte etwas über den Sinn und Zweck deiner Funktion stehen.
Der Nutzer sollte schon wissen, was die Funktion macht und wie er sie benutzen soll.Schau dir mal die Manpage zu strdup an.
-
gut, dann scheint das ja okay zu sein... aber den vorhergehenden beitrag mit der rückgabefunktion habe ich nicht ganz verstanden
-
redox schrieb:
gut, dann scheint das ja okay zu sein... aber den vorhergehenden beitrag mit der rückgabefunktion habe ich nicht ganz verstanden
Ok, habe gerade gelesen, dass du ja einen Zeiger zurueckgeben willst.
Die Funktion weiss doch, ob sie Speicher alloziert hat. Du kannst der Funktion doch einfach einen int-Wert (oder bool C99) als Argument mitgeben, der der Hauptfunktion dann sagt, ob sie den Speicher freigeben soll oder nicht.Oder habe ich das Problem irgendwie falsch verstanden?
-
klar so wäre das möglich. aber die funktion wird immer speicher allozieren, deswegen ist das unnötig.
die frage war ja eher nach dem gutem stil, ob der aufrufende sich unter umständen nicht klargemacht hat, dass er vielleicht irgendwann mal free() rufen muss. bzw. ob es irgendwelche konventionen gibt, solche funktionen zu kennzeichnen.
-
Guter Stil wäre, dass immer die Instanz die Ressourcen freigibt, die sie angefordert hat. Das hat auch gute Gründe, denn damit liegt die Verantwortung immer in einer Programmiererebene und es kann nur etwas schiefgehen, wenn ein Programmierer selber etwas vergisst, nicht weil er eine Doku nicht gründlich gelesen hat. Daher: Niemals mit malloc angeforderten Speicher aus einer Funktion zurück geben! Lass dir für deine Funktion einen Speicherbereich als Parameter übergeben, in den du deine Ergebnisse schreiben kannst.
Bei sehr komplexen Datenstrukturen kannst du auch ein eigenes malloc/free äquivalent anbieten:handle_typ handle = get_handle(); // Konstruiert einen komplexen Datentyp // [... irgendwas damit machen ...] free_handle(handle);
So bleibt die Verantwortung für das korrekte Anlegen und Freigeben des Handles auch auf einer Ebene, ohne dass man als Benutzer genau wissen muss, was ein Handle überhaupt ist.
Orientier dich im Zeifelsfall daran, wie die Standardbibliothek das handhabt (z.B. die Funktionen aus string.h, time.h). Die geben auch nie etwas zurück, was dann noch freigegeben werden muss.
-
ja aber dann gäbe es wieder das problem, dass ich vorher nicht weiss, wie gross der speicherbereich sein wird.
und scheinbar macht strdup(...) aus string.h das ebenfalls so:
The strdup() function allocates sufficient memory for a copy of the string s1, does the copy, and returns a pointer to it. The pointer may subsequently be used as an argument to the function free(3).
-
strdup gehört aber nicht zum C-Standard sondern zu POSIX und BSD.
Wie schon gesagt erfordert das Disziplin den angeforderten Speicher auch wieder freizugeben.
Es ist hilfreich, das in der Instanz zu machen, die ihn angefordert hat.Ob die Funktion nun malloc heißt oder gib_mir_Speicher_fuer_genug_Platz_fuer_meine_Datei ist ziemlich egal.
Wichtig ist doch nur das du an der Stelle wo du diese Funktion aufrufst weißt, das dazu auch eine Freigabe gehört.
-
SeppJ schrieb:
Orientier dich im Zeifelsfall daran, wie die Standardbibliothek das handhabt (z.B. die Funktionen aus string.h, time.h). Die geben auch nie etwas zurück, was dann noch freigegeben werden muss.
Das würde ich nicht tun. Die Standardbibliothek enthält eine Menge historischen Ballast, der allgemein nicht zur Nachahmung empfohlen ist. Das fängt bei der Namensgebung an, umfasst solche Knaller wie gets, und hört beim Ressourcenmanagement (Pointer auf statischen Speicher, ganz großes Kino!) nicht auf. Es gibt gute Gründe, warum die Standardbibliothek so ist, wie sie ist, aber diese Gründe gelten in den meisten Fällen nicht für Applikationscode.
-
hmmm, dann am elegantesten als dritte möglichkeit:
a) speicher der größe n allozieren
b) pointer und n an funktion übergeben
c) funktion schreibt max. n byte ab dem pointer, und gibt bei nicht ausreichenden speicher eine fehlervariable zurück?
-
redox schrieb:
hmmm, dann am elegantesten als dritte möglichkeit:
a) speicher der größe n allozieren
b) pointer und n an funktion übergeben
c) funktion schreibt max. n byte ab dem pointer, und gibt bei nicht ausreichenden speicher eine fehlervariable zurück?
Das ist eine sehr gute Methode, ja.
Ansonsten, wenn dich
ja aber dann gäbe es wieder das problem, dass ich vorher nicht weiss, wie gross der speicherbereich sein wird.
sehr stört, dann eben mit so einem Handle, wie ich es gezeigt habe. Das wird dann aber schon gehoben kompliziert:
vector daten; // Komplexer Datentyp, der auf dynamischen Speicher verweist und sich selber verwaltet construct_vector(&daten); // "Konstruktor" deine_funktion(&daten); destroy_vector(&daten); // "Destruktor"
Viel Spaß beim basteln!
-
Dann mach es aber auch so, dass du einen NULL-Zeiger übergeben kannst und die Funktion den Speicher selber besorgt.
int besorge_speicher(void **p, size_t *n); char *str = NULL; size_t laenge = 0; int status; status = besorge_speicher(&str, &laenge);
Wenn du den Zeiger als Paramter übergibst, musst du einen Zeiger haben.
Den Rückgabewert einer Funktion kann man auch "vergessen" anzugeben.