wake-up-neo.com

printf mit std :: string?

Nach meinem Verständnis ist string ein Mitglied des std-Namespace. Warum tritt also Folgendes auf?

#include <iostream>

int main()
{
    using namespace std;

    string myString = "Press ENTER to quit program!";
    cout << "Come up and C++ me some time." << endl;
    printf("Follow this command: %s", myString);
    cin.get();

    return 0;
}

enter image description here

Bei jeder Ausführung des Programms gibt myString eine scheinbar zufällige Zeichenfolge mit 3 Zeichen aus, wie in der obigen Ausgabe dargestellt.

140
TheDarkIn1978

Es wird kompiliert, weil printf nicht typsicher ist, da es variable Argumente im C-Sinne verwendet1. printf hat keine Option für std::string, nur eine Zeichenfolge im C-Stil. Die Verwendung von etwas anderem anstelle dessen, was erwartet wird, führt definitiv nicht zu den gewünschten Ergebnissen. Es ist eigentlich undefiniertes Verhalten, also kann alles passieren.

Der einfachste Weg, dies zu beheben, da Sie C++ verwenden, besteht darin, es normal mit std::cout Zu drucken, da std::string Dies durch Überladen von Operatoren unterstützt:

std::cout << "Follow this command: " << myString;

Wenn Sie aus irgendeinem Grund die Zeichenfolge im C-Stil extrahieren müssen, können Sie die c_str() -Methode von std::string Verwenden, um eine const char * Zu erhalten, die nullterminiert ist. Verwenden Sie Ihr Beispiel:

#include <iostream>
#include <string>
#include <stdio.h>

int main()
{
    using namespace std;

    string myString = "Press ENTER to quit program!";
    cout << "Come up and C++ me some time." << endl;
    printf("Follow this command: %s", myString.c_str()); //note the use of c_str
    cin.get();

    return 0;
}

Wenn Sie eine Funktion möchten, die printf ähnelt, aber sicher ist, schauen Sie in verschiedene Vorlagen (C++ 11, unterstützt von allen wichtigen Compilern ab MSVC12). Ein Beispiel dafür finden Sie hier . In der Standardbibliothek gibt es nichts, von dem ich so etwas weiß, aber es könnte Boost geben, speziell boost::format .


[1]: Dies bedeutet, dass Sie eine beliebige Anzahl von Argumenten übergeben können, die Funktion jedoch darauf angewiesen ist, dass Sie ihr die Anzahl und den Typ dieser Argumente mitteilen. Im Fall von printf bedeutet dies eine Zeichenfolge mit codierten Typinformationen wie %d, Die int bedeuten. Wenn Sie über den Typ oder die Zahl lügen, gibt es für die Funktion keine Standarderkennungsmethode, obwohl einige Compiler die Möglichkeit haben, zu prüfen und zu warnen, wenn Sie lügen.

214
chris

Bitte benutze nicht printf("%s", your_string.c_str());

Verwenden cout << your_string; stattdessen. Kurz, einfach und typsicher. Wenn Sie C++ schreiben, möchten Sie printf im Allgemeinen ganz vermeiden - es ist ein Rest von C, der in C++ selten benötigt oder nützlich ist.

Was warum betrifft, sollten Sie cout anstelle von printf verwenden, die Gründe dafür sind zahlreich. Hier sind einige der offensichtlichsten Beispiele:

  1. Wie die Frage zeigt, ist printf nicht typsicher. Wenn sich der Typ, den Sie übergeben, von dem im Konvertierungsspezifizierer angegebenen unterscheidet, versucht printf, alles zu verwenden, was sich auf dem Stapel befindet, als wäre es der angegebene Typ, was ein undefiniertes Verhalten ergibt. Einige Compiler können unter bestimmten Umständen davor warnen, aber einige Compiler können/werden es überhaupt nicht und keiner kann es unter allen Umständen.
  2. printf ist nicht erweiterbar. Sie können nur primitive Typen übergeben. Der Satz von Konvertierungsspezifizierern, den es versteht, ist in seiner Implementierung fest codiert, und Sie können keine weiteren/anderen hinzufügen. Die meisten gut geschriebenen C++ sollten diese Typen hauptsächlich verwenden, um Typen zu implementieren, die auf das zu lösende Problem ausgerichtet sind.
  3. Dies erschwert eine anständige Formatierung erheblich. Ein naheliegendes Beispiel: Wenn Sie Zahlen drucken, damit die Benutzer sie lesen können, möchten Sie in der Regel alle paar Ziffern Tausende von Trennzeichen einfügen. Die genaue Anzahl der Ziffern und die Zeichen, die als Trennzeichen verwendet werden, variieren, aber auch cout hat dies abgedeckt. Zum Beispiel:

    std::locale loc("");
    std::cout.imbue(loc);
    
    std::cout << 123456.78;
    

    Das namenlose Gebietsschema (das "") wählt ein Gebietsschema basierend auf der Benutzerkonfiguration aus. Daher wird dies auf meinem Computer (für US-Englisch konfiguriert) als 123,456.78. Für jemanden, der seinen Computer für (sagen wir) Deutschland konfiguriert hat, würde er so etwas wie 123.456,78. Für jemanden, der es für Indien konfiguriert hat, würde es als 1,23,456.78 (und natürlich gibt es viele andere). Mit printf erhalte ich genau ein Ergebnis: 123456.78. Es ist konsistent, aber es ist konsistent falsch für alle überall. Die einzige Möglichkeit, dies zu umgehen, besteht darin, die Formatierung separat vorzunehmen und das Ergebnis dann als Zeichenfolge an printf zu übergeben, da printf selbst dies einfach nicht tut die Arbeit richtig machen.

  4. Obwohl sie recht kompakt sind, können Zeichenfolgen im printf -Format ziemlich unlesbar sein. Selbst unter C-Programmierern, die fast täglich printf verwenden, müssten vermutlich mindestens 99% nachschlagen, um sicherzugehen, was # im %#x bedeutet, und wie unterscheidet sich das von dem, was der # im %#f bedeutet (und ja, sie bedeuten ganz andere Dinge).
40
Jerry Coffin

benutze myString.c_str() wenn du einen c-ähnlichen String willst (const char*) zur Verwendung mit printf

vielen Dank

27

Verwenden Sie std :: printf und c_str () als Beispiel:

std::printf("Follow this command: %s", myString.c_str());
6
Adel Ben Hamadi

Printf ist eigentlich ziemlich gut zu verwenden, wenn es auf die Größe ankommt. Das heißt, wenn Sie ein Programm ausführen, bei dem Speicherprobleme auftreten, ist printf eine sehr gute Lösung. Cout verschiebt im Wesentlichen Bits, um Platz für die Zeichenfolge zu schaffen, während printf nur einige Parameter aufnimmt und auf dem Bildschirm ausgibt. Wenn Sie ein einfaches Hallo-Welt-Programm kompilieren würden, wäre printf in der Lage, es in weniger als 60.000 Bits zu kompilieren, im Gegensatz zu cout. Das Kompilieren würde über 1 Million Bits dauern.

In Ihrer Situation empfehlen wir die Verwendung von cout, weil es viel praktischer ist. Ich würde allerdings argumentieren, dass printf etwas Gutes zu wissen ist.

2
howard howard

printf akzeptiert eine variable Anzahl von Argumenten. Diese können nur POD-Typen (Plain Old Data) haben. Code, der etwas anderes als POD an printf übergibt, wird nur kompiliert, weil der Compiler davon ausgeht, dass Sie Ihr Format richtig eingestellt haben. %s Bedeutet, dass das jeweilige Argument ein Zeiger auf ein char sein soll. In Ihrem Fall ist es ein std::string Nicht const char*. printf weiß es nicht, weil der Argumenttyp verloren geht und aus dem Formatparameter wiederhergestellt werden soll. Wenn Sie dieses Argument std::string In const char* Umwandeln, zeigt der resultierende Zeiger auf einen irrelevanten Speicherbereich anstelle der gewünschten C-Zeichenfolge. Aus diesem Grund druckt Ihr Code Kauderwelsch aus.

Obwohl printfeine ausgezeichnete Wahl zum Ausdrucken von formatiertem Text ist (insbesondere, wenn Sie eine Auffüllung wünschen), kann dies gefährlich sein, wenn Sie Compiler-Warnungen nicht aktiviert haben. Aktivieren Sie immer Warnungen , da solche Fehler dann leicht vermeidbar sind. Es gibt keinen Grund, den ungeschickten Mechanismus std::cout Zu verwenden, wenn die printf -Familie die gleiche Aufgabe viel schneller und schöner erledigen kann. Stellen Sie einfach sicher, dass Sie alle Warnungen aktiviert haben (-Wall -Wextra) Und Sie werden gut sein. Wenn Sie eine eigene benutzerdefinierte printf -Implementierung verwenden, müssen Sie diese mit dem Mechanismus __attribute__ Deklarieren, mit dem der Compiler die Formatzeichenfolge anhand der angegebenen Parameter überprüfen kann .

1
Hyena

Der Hauptgrund ist wahrscheinlich, dass eine C++ - Zeichenfolge eine Struktur ist, die einen Wert der aktuellen Länge enthält und nicht nur die Adresse einer Zeichenfolge, die mit einem 0-Byte abgeschlossen ist. Printf und seine Verwandten erwarten, dass sie eine solche Sequenz finden, keine Struktur, und werden daher von C++ - Strings verwirrt.

Ich persönlich glaube, dass printf eine Stelle hat, die nicht einfach mit syntaktischen C++ - Funktionen gefüllt werden kann, genauso wie Tabellenstrukturen in HTML eine Stelle haben, die nicht einfach mit divs gefüllt werden kann. Wie Dykstra später über das goto schrieb, hatte er nicht die Absicht, eine Religion zu gründen, und sprach sich eigentlich nur dagegen aus, sie als Trick zu benutzen, um schlecht gestalteten Code auszugleichen.

Es wäre ganz nett, wenn das GNU Projekt die printf-Familie zu ihren g ++ - Erweiterungen hinzufügen würde.

1
MMacD