Best Practice zum Zurückgeben einer Zeichenfolge mit variabler Länge in c

8

Ich habe eine Zeichenfolgenfunktion, die einen Zeiger auf eine Quellzeichenfolge akzeptiert und einen Zeiger auf eine Zielzeichenfolge zurückgibt. Diese Funktion funktioniert derzeit, aber ich mache mir Sorgen, dass ich nicht die Best Practices befolge, die malloc, realloc und free neu berechnen.

Was an meiner Funktion anders ist, ist, dass die Länge der Zielzeichenfolge nicht mit der Quellzeichenfolge übereinstimmt, daher muss realloc () innerhalb meiner Funktion aufgerufen werden. Ich weiß von den Dokumenten ...

Ссылка

, dass sich die Speicheradresse nach dem Realloc ändern kann. Das heißt, ich kann nicht wie ein C-Programmierer für andere Funktionen "durch Verweis" weitergeben, ich muss den neuen Zeiger zurückgeben.

Der Prototyp für meine Funktion ist also:

%Vor%

Ich mag es nicht, wie ich es mache, weil ich den Zeiger nach dem Ausführen der Funktion freigeben muss:

%Vor%

Das bedeutet, dass malloc () und realloc () innerhalb meiner Funktion aufgerufen werden und free () außerhalb meiner Funktion aufgerufen wird.

Ich habe einen Hintergrund in Hochsprachen, (Perl, Plpgsql, Bash), also ist mein Instinkt die richtige Verkapselung solcher Dinge, aber das ist vielleicht nicht die beste Übung in C.

Die Frage: Ist mein Weg Best Practice, oder gibt es einen besseren Weg, dem ich folgen sollte?

vollständiges Beispiel

Kompiliert und läuft mit zwei Warnungen für unbenutzte Argumente argc und argv, Sie können diese zwei Warnungen ignorieren.

Beispiel.c:

%Vor%     
Michael 12.06.2013, 17:16
quelle

6 Antworten

8

Es ist vollkommen in Ordnung, malloc 'd Puffer von Funktionen in C zurückzugeben, solange Sie die Tatsache dokumentieren, dass sie dies tun. Viele Bibliotheken tun das, obwohl es keine Funktion in der Standardbibliothek gibt.

Wenn Sie die Anzahl der Zeichen, die in den Puffer geschrieben werden müssen, kostengünstig berechnen können (eine nicht zu pessimistische obere Schranke), können Sie eine Funktion anbieten, die das tut und den Benutzer aufrufen lässt.

Es ist auch möglich, aber viel weniger bequem, einen Puffer zu akzeptieren, der ausgefüllt werden muss; Ich habe schon einige Bibliotheken gesehen, die das so machen:

%Vor%

Jetzt ist der Aufrufer für die Zuweisung verantwortlich und würde etwas wie

tun %Vor%

( xmalloc und xrealloc sind "sichere" Zuweisungsfunktionen, die ich zum Überspringen von NULL-Prüfungen eingerichtet habe.)

    
Fred Foo 12.06.2013, 17:37
quelle
2

Die Sache ist, dass C niedrig genug ist, um den Programmierer dazu zu bringen, ihre Speicherverwaltung richtig zu machen. Insbesondere ist es nicht falsch, eine malloc() ated-Zeichenfolge zurückzugeben. Es ist eine gängige Redewendung, mallocated objects zurückzugeben und den Aufrufer free() sie zu haben.

Und wenn Sie diesen Ansatz nicht mögen, können Sie immer einen Zeiger auf die Zeichenkette nehmen und ihn innerhalb der Funktion ändern (nach der letzten Verwendung muss sie jedoch noch free() d sein) .

Eine Sache jedoch, die ich nicht für notwendig halte, ist das explizite Schrumpfen der Zeichenkette. Wenn die neue Zeichenfolge kürzer als die alte ist, gibt es im Speicherbereich der alten Zeichenfolge offensichtlich genug Platz, sodass Sie realloc() nicht benötigen.

(Abgesehen davon, dass Sie vergessen haben, ein zusätzliches Byte für das abschließende NUL-Zeichen zuzuweisen, natürlich ...)

Und wie immer können Sie jedes Mal, wenn die Funktion aufgerufen wird, einfach einen anderen Zeiger zurückgeben, und Sie müssen nicht einmal realloc() aufrufen.

Wenn Sie einen letzten guten Ratschlag akzeptieren: Es ist ratsam, const -qualifiziert Ihre Eingabezeichenfolgen, damit der Aufrufer sicherstellen kann, dass Sie sie nicht ändern. Mit diesem Ansatz können Sie beispielsweise die Funktion für Zeichenfolgenliterale sicher aufrufen.

Alles in allem würde ich Ihre Funktion folgendermaßen umschreiben:

%Vor%

Und nenne es wie folgt:

%Vor%     
user529758 12.06.2013 17:38
quelle
2

Es ist vollkommen in Ordnung, die Werte von malloc -ed (und möglicherweise intern realloc ed) von Funktionen zurückzugeben, Sie müssen nur dokumentieren, dass Sie dies tun (wie Sie es hier tun).

Andere offensichtliche Dinge:

  • Anstelle von int int_length möchten Sie vielleicht size_t verwenden. Dies ist "ein unsignierter Typ" (normalerweise unsigned int oder unsigned long ), der der geeignete Typ für Längen von Strings und Argumenten für malloc ist.
  • Sie müssen zunächst n + 1 Bytes zuweisen, wobei n die Länge der Zeichenfolge ist, da strlen nicht das abschließende 0 Byte enthält.
  • Sie sollten prüfen, ob malloc fehlgeschlagen ist (Rückgabe von NULL ). Wenn Ihre Funktion den Fehler weitergibt, dokumentieren Sie dies in der Funktionsbeschreibung.
  • sscanf ist ziemlich schwer für die Konvertierung der zwei Hex-Bytes. Nicht falsch , außer dass Sie nicht überprüfen, ob die Konvertierung erfolgreich ist (was ist, wenn die Eingabe fehlerhaft ist? Sie können natürlich entscheiden, dass dies das Problem des Aufrufers ist, aber im Allgemeinen möchten Sie das vielleicht behandeln) . Sie können isxdigit von <ctype.h> verwenden, um nach hexadezimalen Ziffern zu suchen, und / oder strtoul , um die Konvertierung durchzuführen.
  • Anstatt für jede realloc -Konvertierung ein % zu erstellen, möchten Sie möglicherweise einen endgültigen "shrink realloc" durchführen, falls dies gewünscht wird. Beachten Sie, dass wenn Sie (sagen wir) 50 Bytes für eine Zeichenkette zuteilen und feststellen, dass es nur 49 einschließlich der letzten 0 Byte benötigt, es sich möglicherweise nicht lohnt, realloc zu machen.
torek 12.06.2013 17:38
quelle
0

Ich würde das Problem etwas anders angehen. Persönlich würde ich Ihre Funktion in zwei Teile aufteilen. Die erste Funktion, um die Größe zu berechnen, die Sie für malloc benötigen. Die zweite würde den Ausgabe-String in den gegebenen Zeiger schreiben (der außerhalb der Funktion zugewiesen wurde). Das spart mehrere Realloc-Aufrufe und hält die Komplexität gleich. Eine mögliche Funktion, um die Größe der neuen Zeichenfolge zu finden, ist:

%Vor%

Wie in anderen Antworten erwähnt, gibt es jedoch kein Problem bei der Rückgabe eines malloced-Puffers, solange Sie es dokumentieren!

    
pretobomba 12.06.2013 17:47
quelle
0

Zusätzlich, was bereits in den anderen Postings erwähnt wurde, sollten Sie auch die Tatsache dokumentieren, dass die Zeichenfolge neu zugewiesen wird. Wenn Ihr Code mit einer statischen Zeichenfolge oder einer Zeichenfolge aufgerufen wird, die mit alloca zugeordnet ist, können Sie sie nicht neu zuordnen.

    
Devolus 12.06.2013 17:50
quelle
0

Ich denke, du hast recht, wenn es darum geht, Mallocs und Frees zu trennen. In der Regel, was auch immer es macht, besitzt es und sollte es befreien.

In diesem Fall, in dem die Zeichenfolgen relativ klein sind, besteht eine gute Vorgehensweise darin, den Zeichenfolgenpuffer größer als jede mögliche Zeichenfolge zu machen, die er enthalten könnte. Zum Beispiel haben URLs de facto eine Grenze von ungefähr 2000 Zeichen, wenn Sie also 10000 Zeichen malloc sind, können Sie jede mögliche URL speichern.

Ein weiterer Trick besteht darin, sowohl die Länge als auch die Kapazität der Zeichenfolge an der Vorderseite zu speichern, so dass (int)*mystring == length of string und (int)*(mystring + 4) == capacity der Zeichenfolge. Daher beginnt der String selbst erst an der 8. Position *(mystring+8) . Auf diese Weise können Sie einen einzelnen Zeiger auf eine Zeichenfolge übergeben und immer wissen, wie lang diese ist und wie viel Speicherkapazität die Zeichenfolge hat. Sie können Makros erstellen, die diese Offsets automatisch erzeugen und "hübschen Code" erstellen.

Der Wert der Verwendung von Puffern auf diese Weise erfordert keine Neuzuweisung. Der neue Wert überschreibt den alten Wert und Sie aktualisieren die Länge am Anfang der Zeichenfolge.

    
Tyler Durden 12.06.2013 17:51
quelle

Tags und Links