Meinem Verständnis nach ist "String" ein Mitglied des "std"-Namensraums, warum tritt dann 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;
}
Jedes Mal, wenn das Programm läuft, gibt myString
eine scheinbar zufällige Zeichenkette aus 3 Zeichen aus, wie in der obigen Ausgabe.
Es'kompiliert, weil printf
nicht typsicher ist, da es variable Argumente im Sinne von C verwendet1. printf
hat keine Option für std::string
, nur einen String im C-Stil. Die Verwendung von etwas anderem anstelle dessen, was erwartet wird, wird definitiv nicht die gewünschten Ergebnisse liefern. Es ist eigentlich ein undefiniertes Verhalten, es könnte also alles Mögliche passieren.
Der einfachste Weg, dies zu beheben, da Sie C++ verwenden, ist die normale Ausgabe mit std::cout
, da std::string
dies durch Operatorüberladung unterstützt:
std::cout << "Follow this command: " << myString;
Wenn Sie aus irgendeinem Grund die C-artige Zeichenkette extrahieren müssen, können Sie die Methode c_str()
von std::string
verwenden, um ein const char *
zu erhalten, das mit Null abgeschlossen 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 wollen, die wie printf
ist, aber typsicher, schauen Sie sich variadische Templates an (C++11, unterstützt von allen wichtigen Compilern ab MSVC12). Sie können ein Beispiel dafür [hier] finden (https://web.archive.org/web/20131018185034/http://www.generic-programming.org/~dgregor/cpp/variadic-templates.html). Ich weiß nicht, ob so etwas in der Standardbibliothek implementiert ist, aber vielleicht in Boost, speziell boost::format
.
[1]: Das bedeutet, dass Sie eine beliebige Anzahl von Argumenten übergeben können, aber die Funktion verlässt sich darauf, dass Sie ihr die Anzahl und den Typ dieser Argumente mitteilen. Im Fall von printf
bedeutet das eine Zeichenkette mit kodierter Typinformation wie %d
, was int
bedeutet. Wenn Sie bezüglich des Typs oder der Anzahl der Argumente lügen, hat die Funktion keine Möglichkeit, dies herauszufinden, obwohl einige Compiler die Möglichkeit haben, dies zu überprüfen und Warnungen auszugeben, wenn Sie lügen.
Bitte verwenden Sie nicht printf("%s", your_string.c_str());
Verwenden Sie stattdessen cout << your_string;
. Kurz, einfach und typsicher. Wenn Sie C++ schreiben, sollten Sie printf
in der Regel ganz vermeiden - es ist ein Überbleibsel aus C, das in C++ selten gebraucht wird oder nützlich ist.
Für die Frage, warum Sie cout
anstelle von printf
verwenden sollten, gibt es zahlreiche Gründe. Hier's eine Auswahl der offensichtlichsten:
Wie die Frage zeigt, ist printf
nicht typsicher. Wenn der Typ, den Sie übergeben, von dem im Konvertierungsspezifizierer angegebenen Typ abweicht, wird printf
versuchen, das, was es auf dem Stack findet, so zu verwenden, als wäre es der angegebene Typ, was zu undefiniertem Verhalten führt. Einige Compiler können unter bestimmten Umständen davor warnen, aber einige Compiler können/wollen das nicht, und keiner kann das unter allen Umständen.
printf
ist nicht erweiterbar. Man kann ihm nur primitive Typen übergeben. Die Menge der Konvertierungsspezifizierer, die es versteht, ist in seiner Implementierung fest einkodiert, und es gibt keine Möglichkeit, mehr oder andere hinzuzufügen. Die meisten gut geschriebenen C++-Programme sollten diese Typen in erster Linie zur Implementierung von Typen verwenden, die auf das zu lösende Problem ausgerichtet sind.
Es macht eine anständige Formatierung viel schwieriger. Ein offensichtliches Beispiel: Wenn Sie Zahlen zum Lesen ausdrucken, wollen Sie normalerweise alle paar Ziffern Tausendertrennzeichen einfügen. Die genaue Anzahl der Ziffern und die Zeichen, die als Trennzeichen verwendet werden, variieren, aber cout
hat auch das im Griff. Zum Beispiel:
std::locale loc("");
std::cout.imbue(loc);
std::cout << 123456.78;
Das namenlose Gebietsschema (das "") wählt ein Gebietsschema auf der Grundlage der Konfiguration des Benutzers aus. Auf meinem Rechner (der für US-Englisch konfiguriert ist) wird dies als "123.456,78" ausgedruckt. Für jemanden, der seinen Computer für (sagen wir) Deutschland konfiguriert hat, würde es etwas wie "123.456,78" ausgeben. Für jemanden, der seinen Computer für Indien konfiguriert hat, würde es 1.23.456,78
ausgeben (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 und überall. Der einzige Weg, das Problem zu umgehen, ist, die Formatierung separat vorzunehmen und das Ergebnis dann als String an printf
zu übergeben, da printf
selbst die Aufgabe einfach nicht korrekt erledigen kann.
Obwohl sie ziemlich kompakt sind, können Zeichenketten im printf
-Format ziemlich unleserlich sein. Selbst unter C-Programmierern, die printf
praktisch jeden Tag benutzen, schätze ich, dass mindestens 99% nachschlagen müssten, um sicher zu sein, was das #
in %#x
bedeutet und wie sich das von dem unterscheidet, was das #
in %#f
bedeutet (und ja, sie bedeuten völlig unterschiedliche Dinge).
Verwenden Sie myString.c_str()
, wenn Sie eine c-ähnliche Zeichenkette (const char*
) mit printf verwenden wollen
danke