Ich weiß, dass Pufferüberläufe eine potentielle Gefahr für die Verwendung von C-artigen Strings (char-Arrays) darstellen. Wenn ich weiß, dass meine Daten in meinen Puffer passen, ist es in Ordnung, sie trotzdem zu verwenden? Gibt es andere Nachteile von C-Style Strings, denen ich bewusst sein muss?
BEARBEITEN: Hier ist ein Beispiel, an das ich gerade arbeite:
%Vor% Dieser Code nimmt Daten von einem FILE-Zeiger, der mit einem Befehl popen("df")
erstellt wurde. Ich versuche, Linux-Befehle auszuführen und ihre Ausgabe zu analysieren, um Informationen über das Betriebssystem zu erhalten. Gibt es etwas Falsches (oder Gefährliches) beim Einstellen des Puffers auf eine beliebige Größe?
C-Zeichenfolgen fehlen die folgenden Aspekte ihrer C ++ - Gegenstücke:
C-Strings haben einige Nachteile:
Sie wissen vielleicht, dass heute 1024 Bytes ausreichen, um irgendwelche Eingaben zu enthalten, aber Sie wissen nicht, wie sich die Dinge morgen oder nächstes Jahr ändern werden.
Wenn eine vorzeitige Optimierung die Wurzel allen Übels ist, sind magische Zahlen der Stamm.
Die Speicherverwaltung usw., die benötigt wird, um die Zeichenfolge (char-Array) zu vergrößern, ist, wenn nötig, etwas langweilig, um neu zu erfinden.
Nun, um Ihr spezifisches Beispiel zu kommentieren, wissen Sie nicht, dass die von Ihrem Aufruf von df zurückgegebenen Daten in Ihren Puffer passen. Vertrauen Sie niemals un-sanatisierten Eingaben in Ihre Anwendung, selbst wenn sie von einer bekannten Quelle wie df stammen.
Wenn beispielsweise ein Programm mit dem Namen "df" irgendwo in Ihrem Suchpfad platziert wird, sodass es anstelle des System-df ausgeführt wird, könnte es dazu verwendet werden, Ihr Pufferlimit auszunutzen. Oder wenn df durch ein bösartiges Programm ersetzt wird.
Verwenden Sie beim Lesen einer Eingabe aus einer Datei eine Funktion, mit der Sie die maximale Anzahl der zu lesenden Bytes festlegen können. Unter OSX und Linux ist fgets () tatsächlich als char *fgets(char *s, int size, FILE *stream);
definiert, so dass es auf diesen Systemen sicher verwendet werden könnte.
In Ihrem speziellen Fall ist es nicht der C-String, der so gefährlich ist, wie das Lesen einer unbestimmten Menge von Daten in einen Puffer fester Größe. Verwenden Sie zum Beispiel nie gets (char *).
Wenn Sie jedoch Ihr Beispiel betrachten, scheint es überhaupt nicht korrekt zu sein - versuchen Sie Folgendes:
%Vor% Dies ist eine vollkommen sichere Verwendung von C-Strings, obwohl Sie sich damit befassen müssen, dass line
keine ganze Zeile enthält, sondern eher auf 1023 Zeichen gekürzt wurde (plus Null-Terminator).
Ich denke, es ist in Ordnung, sie zu benutzen, die Leute benutzen sie seit Jahren. Aber ich würde lieber std :: string verwenden, wenn möglich 1) Sie müssen nicht jedes Mal so vorsichtig sein und können über Probleme Ihrer Domäne nachdenken, anstatt zu denken, dass Sie jedes Mal einen anderen Parameter hinzufügen müssen ... Speicherverwaltung und das ein bisschen Zeug ... es ist nur sicherer auf höherer Ebene programmieren ... 2) Es gibt wahrscheinlich noch andere kleine Probleme, die keine große Sache sind, aber immer noch ... wie die Leute schon erwähnt haben ... encoding, unicode ... all diese "verwandten" Sachen Leute, die std :: string gedacht haben ... :)
Aktualisieren
Ich habe ein halbes Jahr an einem Projekt gearbeitet. Irgendwie war ich blöd genug, um im Release-Modus vor der Auslieferung nicht zu kompilieren .... :) Nun, zum Glück gab es nur einen Fehler, den ich nach 3 Stunden gefunden habe. Es war ein sehr einfacher String Buffer Overrun.
c strings haben Möglichkeiten für einen Missbrauch, da dieser die Zeichenfolge scannen muss, um festzustellen, wo sie endet.
strlen - Um die Länge zu finden, scannen Sie die Zeichenfolge, bis Sie die NUL drücken oder auf geschützten Speicher zugreifen
strcat - muss scannen, um die NUL zu finden, um zu bestimmen, wo die Verkettung beginnen soll. Es gibt kein Wissen innerhalb einer c-Zeichenfolge, um festzustellen, ob ein Pufferüberlauf vorliegt oder nicht.
c-Zeichenfolgen sind riskant, aber in der Regel schneller als Zeichenfolgenobjekte.
Imho, der schwierigste Punkt von cstrings ist die Speicherverwaltung, weil Sie vorsichtig sein müssen, wenn Sie eine Kopie eines cstring übergeben müssen oder wenn Sie ein Literal an eine Funktion übergeben können, dh. Wird die Funktion die übergebene Zeichenfolge freigeben oder wird sie länger als der Funktionsaufruf beibehalten? Dasselbe gilt für Rückgabewerte von cstring.
Es ist also ohne großen Aufwand nicht möglich, cstring-Copys zu teilen. Dies endet in vielen Fällen mit unnötiger Kopie des gleichen cstring im Speicher.
Diese Frage hat wirklich keine Antwort.
Wenn Sie in C schreiben, was über Optionen haben Sie?
Wenn Sie in C ++ schreiben, warum fragen Sie? Was ist der Grund, C ++ - Primitive nicht zu verwenden?
Der einzige Grund, den ich denken kann, ist: C- und C ++ - Code verknüpfen und char * irgendwo in Schnittstellen haben. Manchmal ist es einfach, char * zu verwenden und stattdessen die ganze Zeit zurück und vorwärts zu konvertieren (besonders, wenn es wirklich 'guter' C ++ - Code ist, der 3 verschiedene C ++ - String-Objekttypen hat).
C-Saiten, wie viele andere Aspekte von C, geben Ihnen viel Raum, um sich aufzuhängen. Sie sind einfach und schnell, aber in der Situation unsicher, in der Annahmen wie der Null-Terminator verletzt werden können oder die Eingabe den Puffer überlaufen kann. Um sie zuverlässig zu machen, müssen Sie ziemlich hygenische Kodierungspraktiken beachten.
Früher gab es ein Sprichwort, dass die kanonische Definition einer Hochsprache "etwas mit besserer String-Handhabung als C" ist.
Eine weitere Überlegung ist, wer Ihren Code pflegen wird? Wie sieht es in zwei Jahren aus? Wird diese Person mit C-stlye-Saiten so vertraut sein wie du? Wenn die STL reifer wird, scheint es, dass die Leute mit STL-Streichern immer mehr vertraut sind als mit C-Style-Streichern.