Ich verwende viel printf
für das Verfolgen / Protokollieren in meinem Code. Ich habe festgestellt, dass dies eine Quelle für Programmierfehler ist. Ich fand den Einfügeoperator ( <<
) immer etwas seltsam, aber ich fange an zu denken, dass ich einige dieser Fehler vermeiden könnte, wenn ich ihn stattdessen verwende.
Hat jemand jemals eine ähnliche Offenbarung gehabt oder greife ich hier nur nach Strohhalmen?
Einige nehmen Punkte weg
- Meine derzeitige Meinung ist, dass die Typensicherheit den Nutzen der Verwendung von printf überwiegt. Das eigentliche Problem ist die Formatzeichenfolge und die Verwendung nicht typsicherer variabler Funktionen.
- Vielleicht werde ich
<<
die stl-Ausgabestream-Varianten nicht verwenden, aber ich werde auf jeden Fall einen typsicheren Mechanismus verwenden, der sehr ähnlich ist. - Ein Großteil der Ablaufverfolgung / Protokollierung ist an Bedingungen geknüpft, aber ich möchte den Code immer ausführen, um Fehler in Tests nicht zu verpassen, nur weil es sich um einen selten verwendeten Zweig handelt.
printf
in der C ++ Welt? Ich vermisse hier etwas?printf
in C ++ ist völlig legal . (Ob es eine gute Idee ist, ist eine andere Frage.)printf
hat einige Vorteile; siehe meine Antwort.Antworten:
printf ist besonders in Fällen, in denen Sie sich für die Leistung interessieren (wie sprintf und fprintf), ein wirklich seltsamer Hack. Es überrascht mich immer wieder, dass Leute, die aufgrund des geringen Leistungsaufwands im Zusammenhang mit virtuellen Funktionen auf C ++ hämmern, dann Cs Io verteidigen.
Ja, um das Format unserer Ausgabe herauszufinden, das wir zur Kompilierungszeit zu 100% kennen, analysieren wir zur Laufzeit eine verdammte Formatzeichenfolge in einer massiv seltsamen Sprungtabelle mit unergründlichen Formatcodes!
Natürlich konnten diese Formatcodes nicht so erstellt werden, dass sie mit den Typen übereinstimmen, die sie darstellen. Das wäre zu einfach ... und Sie werden jedes Mal daran erinnert, wenn Sie nachschlagen, ob es sich um% llg oder% lg handelt, dass diese (stark typisierte) Sprache Sie dazu bringt Finden Sie Typen manuell heraus, um etwas zu drucken / zu scannen, UND wurde für Prozessoren vor 32 Bit entwickelt.
Ich gebe zu, dass C ++ mit der Formatbreite und -präzision sperrig umgeht und syntaktischen Zucker verbrauchen könnte, aber das bedeutet nicht, dass Sie den bizarren Hack verteidigen müssen, der das Haupt-Io-System von C ist. Die absoluten Grundlagen sind in beiden Sprachen ziemlich einfach (obwohl Sie wahrscheinlich sowieso so etwas wie eine benutzerdefinierte Fehlerfunktion / einen benutzerdefinierten Fehlerstrom für den Debug-Code verwenden sollten), die moderaten Fälle sind in C regex-artig (einfach zu schreiben, schwer zu analysieren / zu debuggen) ) und die komplexen Fälle, die in C. unmöglich sind.
(Wenn Sie überhaupt die Standardcontainer verwenden, schreiben Sie sich einige schnelle Vorlagenüberladungen für Operatoren <<, mit denen Sie beispielsweise das
std::cout << my_list << "\n";
Debuggen durchführen können, bei dem my_list vom Typ istlist<vector<pair<int,string> > >
.)quelle
operator<<(ostream&, T)
durch Aufrufen implementiert werden ... na jasprintf
! Die Leistung vonsprintf
ist nicht optimal, aber aufgrund dessen ist die Leistung von iostreams im Allgemeinen noch schlechter.Das Mischen der Ausgabe im C-Stil
printf()
(oderputs()
oderputchar()
oder ...) mit der Ausgabe im C ++ - Stilstd::cout << ...
kann unsicher sein. Wenn ich mich richtig erinnere, können sie separate Puffermechanismen haben, sodass die Ausgabe möglicherweise nicht in der beabsichtigten Reihenfolge angezeigt wird. (Wie AProgrammer in einem Kommentar erwähnt,sync_with_stdio
spricht dies an).printf()
ist grundsätzlich typunsicher. Der für ein Argument erwartete Typ wird durch die Formatzeichenfolge bestimmt ("%d"
erfordert eineint
oder etwas, zu dem befördert wirdint
,"%s"
erfordert eine,char*
die auf eine korrekt terminierte Zeichenfolge im C-Stil verweisen muss usw.), aber das Übergeben des falschen Argumenttyps führt zu undefiniertem Verhalten , kein diagnostizierbarer Fehler. Einige Compiler, wie z. B. gcc, warnen recht gut vor Typinkongruenzen, können dies jedoch nur, wenn die Formatzeichenfolge ein Literal ist oder zur Kompilierungszeit anderweitig bekannt ist (was der häufigste Fall ist) - und so weiter Warnungen werden von der Sprache nicht benötigt. Wenn Sie die falsche Art von Argument übergeben, können willkürlich schlechte Dinge passieren.Die Stream-E / A von C ++ sind dagegen viel typsicherer, da der
<<
Operator für viele verschiedene Typen überlastet ist.std::cout << x
muss nicht den Typ angebenx
; Der Compiler generiert den richtigen Code für jeden Typx
.Auf der anderen Seite sind
printf
die Formatierungsoptionen meiner Meinung nach viel praktischer. Wenn ich einen Gleitkommawert mit 3 Nachkommastellen drucken möchte, kann ich ihn verwenden"%.3f"
- und er hat keine Auswirkungen auf andere Argumente, selbst innerhalb desselbenprintf
Aufrufs. C ++ 'ssetprecision
wirkt sich dagegen auf den Status des Streams aus und kann die spätere Ausgabe durcheinander bringen, wenn Sie nicht sehr vorsichtig sind, um den vorherigen Status des Streams wiederherzustellen. (Dies ist mein persönlicher Ärger mit Haustieren. Wenn ich einen sauberen Weg vermisse, um ihn zu vermeiden, kommentieren Sie ihn bitte.)Beide haben Vor- und Nachteile. Die Verfügbarkeit von
printf
ist besonders nützlich, wenn Sie zufällig einen C-Hintergrund haben und mit diesem besser vertraut sind oder wenn Sie C-Quellcode in ein C ++ - Programm importieren.std::cout << ...
ist für C ++ idiomatischer und erfordert nicht so viel Sorgfalt, um Typinkongruenzen zu vermeiden. Beide sind gültiges C ++ (der C ++ - Standard enthält den größten Teil der C-Standardbibliothek als Referenz).Es ist wahrscheinlich am besten, es
std::cout << ...
für andere C ++ - Programmierer zu verwenden, die möglicherweise an Ihrem Code arbeiten, aber Sie können beide verwenden - insbesondere in Trace-Code, den Sie wegwerfen werden.Und natürlich lohnt es sich, etwas Zeit mit dem Umgang mit Debuggern zu verbringen (aber in einigen Umgebungen ist dies möglicherweise nicht möglich).
quelle
printf
.std::ios_base::sync_with_stdio
.std::cout
für jedes gedruckte Element ein separater Aufruf verwendet wird? Sie können dies umgehen, indem Sie vor dem Drucken eine Ausgabezeile in einer Zeichenfolge sammeln. Und natürlich können Sie auch jeweils einen Artikel mit druckenprintf
. Es ist nur bequemer, eine Zeile (oder mehrere) in einem Anruf zu drucken.Ihr Problem ist höchstwahrscheinlich auf die Mischung von zwei sehr unterschiedlichen Standard-Output-Managern zurückzuführen, von denen jeder seine eigene Agenda für dieses arme kleine STDOUT hat. Sie erhalten keine Garantie dafür, wie sie implementiert sind, und es ist durchaus möglich, dass sie widersprüchliche Dateideskriptoroptionen festlegen, beide versuchen, unterschiedliche Aktionen auszuführen usw. Außerdem haben die Einfügeoperatoren einen großen Vorteil
printf
:printf
Sie können dies tun:Während
<<
nicht.Hinweis: Zum Debuggen verwenden Sie nicht
printf
odercout
. Sie verwendenfprintf(stderr, ...)
undcerr
.quelle
printf
Typensicherheit nicht typsicher ist, und ich bin der aktuellen Meinung, dass die Typensicherheit den Nutzen der Verwendung überwiegtprintf
. Das Problem ist wirklich das Format - String und die nicht typsichere variadische Funktion.SomeObject
es kein Zeiger ist? Sie erhalten beliebige Binärdaten, die der Compiler darstelltSomeObject
.Es gibt viele Gruppen - zum Beispiel Google -, die keine Streams mögen.
http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Streams
(Öffnen Sie das Dreieck-Ding, damit Sie die Diskussion sehen können.) Ich denke, der Google C ++ - Styleguide enthält VIELE sehr vernünftige Ratschläge.
Ich denke, der Nachteil ist, dass Streams sicherer sind, aber printf klarer zu lesen ist (und es einfacher ist, genau die gewünschte Formatierung zu erhalten).
quelle
printf
kann aufgrund mangelnder Typensicherheit Fehler verursachen. Es gibt ein paar Möglichkeiten der Adressierung dass ohne Schaltiostream
‚s<<
Betreiber und kompliziertere Formatierung:printf
derprintf
Argumente überprüfen und Warnungen wie die folgenden anzeigen, wenn sie nicht übereinstimmen.printf
Aufrufe im Stil vorverarbeiten , um sie typsicher zu machen.printf
ähnliche Formatzeichenfolgen verwenden (insbesondere die von Boost.Format sind fast identisch mitprintf
), während die Typensicherheit und die Typerweiterbarkeit erhalten bleibeniostreams
.quelle
Die Printf-Syntax ist grundsätzlich in Ordnung, abzüglich einiger unklarer Eingaben. Wenn Sie denken, dass es falsch ist, warum C #, Python und andere Sprachen das sehr ähnliche Konstrukt verwenden? Das Problem in C oder C ++: Es ist nicht Teil einer Sprache und wird daher vom Compiler nicht auf korrekte Syntax (*) überprüft und bei Optimierung der Geschwindigkeit nicht in eine Reihe nativer Aufrufe zerlegt. Beachten Sie, dass bei einer Größenoptimierung printf-Aufrufe möglicherweise effizienter werden! Die C ++ - Streaming-Syntax ist alles andere als gut. Es funktioniert, Typensicherheit ist da, aber die ausführliche Syntax ... bleh. Ich meine, ich benutze es, aber ohne Freude.
(*) Einige Compiler führen diese Überprüfung sowie fast alle statischen Analysewerkzeuge durch (ich verwende Lint und hatte seitdem keine Probleme mit printf).
quelle
format("fmt") % arg1 % arg2 ...;
) mit Typensicherheit kombiniert . Auf Kosten einer höheren Leistung, da Stringstream-Aufrufe generiert werden, die in vielen Implementierungen intern Sprintf-Aufrufe generieren.printf
ist meiner Meinung nach ein weitaus flexibleres Ausgabewerkzeug für den Umgang mit Variablen als alle CPP-Stream-Ausgaben. Beispielsweise:Möglicherweise möchten Sie den CPP-
<<
Operator jedoch verwenden, wenn Sie ihn für eine bestimmte Methode überladen ... um beispielsweise einen Speicherauszug eines Objekts abzurufen, das die Daten einer bestimmten Person enthältPersonData
.Dafür wäre es weitaus effektiver zu sagen (vorausgesetzt, es
a
handelt sich um ein Objekt von PersonData).als:
Ersteres entspricht weitaus mehr dem Prinzip der Kapselung (keine Notwendigkeit, die Einzelheiten zu kennen, private Mitgliedsvariablen) und ist auch leichter zu lesen.
quelle
Sie sollten nicht
printf
in C ++ verwenden. Je. Der Grund ist, wie Sie richtig bemerkt haben, dass es eine Fehlerquelle ist und die Tatsache, dass das Drucken von benutzerdefinierten Typen und in C ++ fast alles benutzerdefinierte Typen sein sollte, schmerzhaft ist. C ++ - Lösung sind die Streams.Es gibt jedoch ein kritisches Problem, das Streams für eine vom Benutzer sichtbare Ausgabe ungeeignet macht! Das Problem sind Übersetzungen. Ein Ausleihbeispiel aus dem gettext-Handbuch besagt, dass Sie schreiben möchten:
Jetzt kommt der deutsche Übersetzer und sagt: Ok, auf Deutsch sollte die Nachricht sein
Und jetzt bist du in Schwierigkeiten, weil er die Teile herummischen muss. Es sollte gesagt werden, dass sogar viele Implementierungen
printf
Probleme damit haben. Es sei denn, sie unterstützen die Erweiterung, damit Sie sie verwenden könnenDas Boost.Format unterstützt Printf -Formate und verfügt über diese Funktion. Also schreibst du:
Leider ist die Leistung etwas beeinträchtigt, da intern ein Stringstream erstellt wird und der
<<
Operator zum Formatieren jedes Bits verwendet wird. In vielen Implementierungen<<
ruft der Operator intern aufsprintf
. Ich vermute, dass eine effizientere Implementierung möglich wäre, wenn dies wirklich gewünscht wäre.quelle
Sie erledigen eine Menge nutzloser Arbeit, abgesehen von der Tatsache, ob
stl
böse oder nicht, debuggen Sie Ihren Code mit einer Reihe vonprintf
nur 1 weiteren möglichen Fehlern.Verwenden Sie einfach einen Debugger und lesen Sie etwas über Ausnahmen und wie man sie fängt und wirft. Versuche nicht ausführlicher zu sein, als du eigentlich sein musst.
PS
printf
wird in C für das C ++ verwendet, das Sie habenstd::cout
quelle