Antworten:
Ich bin überrascht, dass jeder in dieser Frage behauptet, das std::cout
sei viel besser als printf
, auch wenn die Frage nur nach Unterschieden gefragt hat. Jetzt gibt es einen Unterschied - std::cout
ist C ++ und printf
ist C (Sie können es jedoch in C ++ verwenden, genau wie fast alles andere von C). Jetzt werde ich hier ehrlich sein; beide printf
und std::cout
haben ihre Vorteile.
std::cout
ist erweiterbar. Ich weiß, dass die Leute sagen werden, dass dies auch printf
erweiterbar ist, aber eine solche Erweiterung wird im C-Standard nicht erwähnt (Sie müssten also nicht standardmäßige Funktionen verwenden - aber es gibt nicht einmal übliche nicht standardmäßige Funktionen), und solche Erweiterungen bestehen aus einem Buchstaben (so ist es leicht, mit einem bereits vorhandenen Format in Konflikt zu geraten).
Im Gegensatz zu printf
, std::cout
vollständig auf Operatorüberladung abhängt, so ist es kein Problem mit benutzerdefinierten Formaten - alles , was Sie tun , ist ein Unterprogramm Mitnahmen definieren std::ostream
als erstes Argument und Ihre Art als zweites. Daher gibt es keine Namespace-Probleme. Solange Sie eine Klasse haben (die nicht auf ein Zeichen beschränkt ist), können Sie arbeitenstd::ostream
Überladung kommen.
Ich bezweifle jedoch, dass viele Leute erweitern möchten ostream
(um ehrlich zu sein, habe ich solche Erweiterungen selten gesehen, auch wenn sie einfach zu erstellen sind). Es ist jedoch hier, wenn Sie es brauchen.
Wie leicht zu erkennen war, verwenden beide printf
und std::cout
unterschiedliche Syntax. printf
Verwendet die Standardfunktionssyntax unter Verwendung von Musterzeichenfolgen und Argumentlisten mit variabler Länge. Tatsächlich printf
ist dies ein Grund, warum C sie hat - printf
Formate sind zu komplex, um ohne sie verwendet werden zu können. Verwendet std::cout
jedoch eine andere API - dieoperator <<
API, die sich selbst zurückgibt.
Im Allgemeinen bedeutet dies, dass die C-Version kürzer ist, aber in den meisten Fällen spielt dies keine Rolle. Der Unterschied macht sich bemerkbar, wenn Sie viele Argumente drucken. Wenn Sie unter der Error 2: File not found.
Annahme einer Fehlernummer etwas schreiben müssen und die Beschreibung einen Platzhalter enthält, sieht der Code folgendermaßen aus. Beide Beispiele funktionieren identisch (nun, irgendwie wird std::endl
der Puffer tatsächlich geleert).
printf("Error %d: %s.\n", id, errors[id]);
std::cout << "Error " << id << ": " << errors[id] << "." << std::endl;
Dies scheint zwar nicht zu verrückt zu sein (es ist nur zweimal länger), aber die Dinge werden verrückter, wenn Sie Argumente tatsächlich formatieren, anstatt sie nur zu drucken. Zum Beispiel ist das Drucken von so etwas 0x0424
einfach verrückt. Dies wird durch den std::cout
Mischzustand und die tatsächlichen Werte verursacht. Ich habe nie eine Sprache gesehen, in der so etwas wie std::setfill
ein Typ wäre (außer natürlich C ++). printf
trennt Argumente und tatsächlichen Typ klar voneinander. Ich würde es wirklich vorziehen, die printf
Version davon beizubehalten (auch wenn sie irgendwie kryptisch aussieht) im Vergleich zur iostream
Version davon (da sie zu viel Rauschen enthält).
printf("0x%04x\n", 0x424);
std::cout << "0x" << std::hex << std::setfill('0') << std::setw(4) << 0x424 << std::endl;
Hier liegt der wahre Vorteil von printf
. Die printf
Formatzeichenfolge ist gut ... eine Zeichenfolge. Das macht es wirklich einfach zu übersetzen, verglichen mit operator <<
Missbrauch von iostream
. Angenommen, die gettext()
Funktion übersetzt Error 2: File not found.
den Code, um die Übersetzung der zuvor angezeigten Formatzeichenfolge zu erhalten , und Sie möchten ihn anzeigen , sieht folgendermaßen aus:
printf(gettext("Error %d: %s.\n"), id, errors[id]);
Nehmen wir nun an, wir übersetzen in Fictionish, wobei die Fehlernummer nach der Beschreibung steht. Die übersetzte Zeichenfolge würde so aussehen %2$s oru %1$d.\n
. Wie geht das in C ++? Nun, ich habe keine Ahnung. Ich denke, Sie können Fälschungen machen, an iostream
die Sie Konstrukte weitergeben printf
können gettext
, oder etwas anderes, um sie zu übersetzen. Natürlich $
ist es kein C-Standard, aber es ist so häufig, dass es meiner Meinung nach sicher ist.
C hat viele ganzzahlige Typen, ebenso wie C ++. std::cout
behandelt alle Typen für Sie, printf
erfordert jedoch abhängig von einem Integer-Typ eine bestimmte Syntax (es gibt Nicht-Integer-Typen, aber der einzige Nicht-Integer-Typ, mit dem Sie in der Praxis arbeiten, printf
ist const char *
(C-Zeichenfolge, kann mit der to_c
Methode von erhalten werden std::string
)). Zum Drucken size_t
müssen Sie beispielsweise verwenden %zd
, während int64_t
die Verwendung erforderlich ist %"PRId64"
. Die Tabellen sind unter http://en.cppreference.com/w/cpp/io/c/fprintf und http://en.cppreference.com/w/cpp/types/integer verfügbar .
\0
Da printf
im Gegensatz zu C ++ - Zeichenfolgen C-Zeichenfolgen verwendet werden, kann kein NUL-Byte ohne bestimmte Tricks gedruckt werden. In bestimmten Fällen ist die Verwendung %c
mit möglich'\0'
als Argument zu verwenden, obwohl dies eindeutig ein Hack ist.
Update: Es stellt sich heraus, dass iostream
es so langsam ist, dass es normalerweise langsamer als Ihre Festplatte ist (wenn Sie Ihr Programm in eine Datei umleiten). Das Deaktivieren der Synchronisierung mit stdio
kann hilfreich sein, wenn Sie viele Daten ausgeben müssen. Wenn die Leistung ein echtes Problem darstellt (im Gegensatz zum Schreiben mehrerer Zeilen in STDOUT), verwenden Sie einfach printf
.
Jeder denkt, dass ihm die Leistung am Herzen liegt, aber niemand stört sich daran, sie zu messen. Meine Antwort ist, dass E / A sowieso ein Engpass ist, egal ob Sie printf
oder verwenden iostream
. Ich denke, das printf
könnte von einem kurzen Blick in die Assembly schneller sein (kompiliert mit clang unter Verwendung der -O3
Compiler-Option). Unter der Annahme meines Fehlerbeispiels führt das printf
Beispiel weit weniger Anrufe aus als das cout
Beispiel. Dies ist int main
mit printf
:
main: @ @main
@ BB#0:
push {lr}
ldr r0, .LCPI0_0
ldr r2, .LCPI0_1
mov r1, #2
bl printf
mov r0, #0
pop {lr}
mov pc, lr
.align 2
@ BB#1:
Sie können leicht feststellen, dass zwei Zeichenfolgen und 2
(Zahl) als printf
Argumente verwendet werden. Das ist alles; es gibt nichts anderes. Zum Vergleich wird dies iostream
zur Baugruppe kompiliert. Nein, es gibt kein Inlining. Jeder einzelne operator <<
Aufruf bedeutet einen weiteren Aufruf mit einem anderen Satz von Argumenten.
main: @ @main
@ BB#0:
push {r4, r5, lr}
ldr r4, .LCPI0_0
ldr r1, .LCPI0_1
mov r2, #6
mov r3, #0
mov r0, r4
bl _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
mov r0, r4
mov r1, #2
bl _ZNSolsEi
ldr r1, .LCPI0_2
mov r2, #2
mov r3, #0
mov r4, r0
bl _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
ldr r1, .LCPI0_3
mov r0, r4
mov r2, #14
mov r3, #0
bl _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
ldr r1, .LCPI0_4
mov r0, r4
mov r2, #1
mov r3, #0
bl _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
ldr r0, [r4]
sub r0, r0, #24
ldr r0, [r0]
add r0, r0, r4
ldr r5, [r0, #240]
cmp r5, #0
beq .LBB0_5
@ BB#1: @ %_ZSt13__check_facetISt5ctypeIcEERKT_PS3_.exit
ldrb r0, [r5, #28]
cmp r0, #0
beq .LBB0_3
@ BB#2:
ldrb r0, [r5, #39]
b .LBB0_4
.LBB0_3:
mov r0, r5
bl _ZNKSt5ctypeIcE13_M_widen_initEv
ldr r0, [r5]
mov r1, #10
ldr r2, [r0, #24]
mov r0, r5
mov lr, pc
mov pc, r2
.LBB0_4: @ %_ZNKSt5ctypeIcE5widenEc.exit
lsl r0, r0, #24
asr r1, r0, #24
mov r0, r4
bl _ZNSo3putEc
bl _ZNSo5flushEv
mov r0, #0
pop {r4, r5, lr}
mov pc, lr
.LBB0_5:
bl _ZSt16__throw_bad_castv
.align 2
@ BB#6:
Um ehrlich zu sein, bedeutet dies jedoch nichts, da E / A ohnehin der Engpass ist. Ich wollte nur zeigen, dass iostream
das nicht schneller ist, weil es "typsicher" ist. Die meisten C-Implementierungen implementieren printf
Formate mit berechnetem goto, so dass dies printf
so schnell wie möglich ist, auch ohne dass der Compiler sich dessen bewusst ist printf
(nicht, dass dies nicht der printf
Fall ist - einige Compiler können dies in bestimmten Fällen optimieren). Konstante Zeichenfolgen, die mit enden, werden \n
normalerweise auf optimiertputs
). .
Ich weiß nicht, warum Sie erben möchten ostream
, aber es ist mir egal. Das ist auch mit möglich FILE
.
class MyFile : public FILE {}
Richtig, Argumentlisten mit variabler Länge haben keine Sicherheit, aber das spielt keine Rolle, da gängige C-Compiler Probleme mit printf
Formatzeichenfolgen erkennen können, wenn Sie Warnungen aktivieren. Tatsächlich kann Clang dies tun, ohne Warnungen zu aktivieren.
$ cat safety.c
#include <stdio.h>
int main(void) {
printf("String: %s\n", 42);
return 0;
}
$ clang safety.c
safety.c:4:28: warning: format specifies type 'char *' but the argument has type 'int' [-Wformat]
printf("String: %s\n", 42);
~~ ^~
%d
1 warning generated.
$ gcc -Wall safety.c
safety.c: In function ‘main’:
safety.c:4:5: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
printf("String: %s\n", 42);
^
std::sort
, was im Vergleich zu qsort
(2-mal) irgendwie überraschend schnell ist Kosten der ausführbaren Größe).
Aus den C ++ - FAQ :
[15.1] Warum sollte ich
<iostream>
anstelle des traditionellen verwenden<cstdio>
?Erhöhen Sie die Typensicherheit, reduzieren Sie Fehler, ermöglichen Sie Erweiterbarkeit und sorgen Sie für Vererbbarkeit.
printf()
ist wohl nicht kaputt undscanf()
vielleicht lebensfähig, obwohl es fehleranfällig ist, aber beide sind in Bezug auf die Möglichkeiten von C ++ I / O begrenzt. C ++ I / O (mit<<
und>>
) ist relativ zu C (mitprintf()
undscanf()
):
<iostream>
Typensicherer : Mit ist der Typ des Objekts, für das E / A-Vorgänge ausgeführt werden, dem Compiler statisch bekannt. Im Gegensatz dazu<cstdio>
werden "%" -Felder verwendet, um die Typen dynamisch zu ermitteln.- Weniger fehleranfällig: Mit
<iostream>
gibt es keine redundanten "%" -Token, die mit den tatsächlichen Objekten übereinstimmen müssen, für die E / A-Vorgänge ausgeführt werden. Durch das Entfernen der Redundanz wird eine Fehlerklasse entfernt.- Erweiterbar: Der C ++ -
<iostream>
Mechanismus ermöglicht die E / A-Bearbeitung neuer benutzerdefinierter Typen, ohne dass vorhandener Code beschädigt wird. Stellen Sie sich das Chaos vor, wenn alle gleichzeitig neue inkompatible "%" -Felder zuprintf()
undscanf()
?! Hinzufügen.- Vererbbar: Der C ++ -
<iostream>
Mechanismus basiert auf realen Klassen wiestd::ostream
undstd::istream
. Im Gegensatz zu<cstdio>
'sFILE*
sind dies echte Klassen und daher vererbbar. Dies bedeutet, dass Sie andere benutzerdefinierte Dinge haben können, die wie Streams aussehen und sich verhalten, aber dennoch alle seltsamen und wunderbaren Dinge tun, die Sie wollen. Sie können automatisch die zig Millionen Zeilen E / A-Code verwenden, die von Benutzern geschrieben wurden, die Sie nicht einmal kennen, und die nichts über Ihre "Extended Stream" -Klasse wissen müssen.
Auf der anderen Seite printf
ist es erheblich schneller, was es rechtfertigen kann, es cout
in sehr spezifischen und begrenzten Fällen vorzuziehen. Profil immer zuerst. (Siehe zum Beispiel http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /)
printf()
soll auch erweiterbar sein. Siehe "printf hooks" unter udrepper.livejournal.com/20948.html
printf
hat keine solche Fähigkeit. Nicht tragbare Bibliotheksmechanismen sind kaum auf dem Niveau der vollständig standardisierten Erweiterbarkeit von iostreams.
Die Leute behaupten oft, das printf
sei viel schneller. Dies ist größtenteils ein Mythos. Ich habe es gerade mit den folgenden Ergebnissen getestet:
cout with only endl 1461.310252 ms
cout with only '\n' 343.080217 ms
printf with only '\n' 90.295948 ms
cout with string constant and endl 1892.975381 ms
cout with string constant and '\n' 416.123446 ms
printf with string constant and '\n' 472.073070 ms
cout with some stuff and endl 3496.489748 ms
cout with some stuff and '\n' 2638.272046 ms
printf with some stuff and '\n' 2520.318314 ms
Fazit: Wenn Sie nur Zeilenumbrüche wünschen, verwenden Sie printf
; Ansonsten cout
ist es fast genauso schnell oder sogar noch schneller. Weitere Details finden Sie in meinem Blog .
Um klar zu sein, ich versuche nicht zu sagen, dass iostream
s immer besser sind als printf
; Ich versuche nur zu sagen, dass Sie eine fundierte Entscheidung treffen sollten, die auf realen Daten basiert, und keine wilde Vermutung, die auf einer allgemeinen, irreführenden Annahme basiert.
Update: Hier ist der vollständige Code, den ich zum Testen verwendet habe. Kompiliert mit g++
ohne zusätzliche Optionen (außer -lrt
für das Timing).
#include <stdio.h>
#include <iostream>
#include <ctime>
class TimedSection {
char const *d_name;
timespec d_start;
public:
TimedSection(char const *name) :
d_name(name)
{
clock_gettime(CLOCK_REALTIME, &d_start);
}
~TimedSection() {
timespec end;
clock_gettime(CLOCK_REALTIME, &end);
double duration = 1e3 * (end.tv_sec - d_start.tv_sec) +
1e-6 * (end.tv_nsec - d_start.tv_nsec);
std::cerr << d_name << '\t' << std::fixed << duration << " ms\n";
}
};
int main() {
const int iters = 10000000;
char const *text = "01234567890123456789";
{
TimedSection s("cout with only endl");
for (int i = 0; i < iters; ++i)
std::cout << std::endl;
}
{
TimedSection s("cout with only '\\n'");
for (int i = 0; i < iters; ++i)
std::cout << '\n';
}
{
TimedSection s("printf with only '\\n'");
for (int i = 0; i < iters; ++i)
printf("\n");
}
{
TimedSection s("cout with string constant and endl");
for (int i = 0; i < iters; ++i)
std::cout << "01234567890123456789" << std::endl;
}
{
TimedSection s("cout with string constant and '\\n'");
for (int i = 0; i < iters; ++i)
std::cout << "01234567890123456789\n";
}
{
TimedSection s("printf with string constant and '\\n'");
for (int i = 0; i < iters; ++i)
printf("01234567890123456789\n");
}
{
TimedSection s("cout with some stuff and endl");
for (int i = 0; i < iters; ++i)
std::cout << text << "01234567890123456789" << i << std::endl;
}
{
TimedSection s("cout with some stuff and '\\n'");
for (int i = 0; i < iters; ++i)
std::cout << text << "01234567890123456789" << i << '\n';
}
{
TimedSection s("printf with some stuff and '\\n'");
for (int i = 0; i < iters; ++i)
printf("%s01234567890123456789%i\n", text, i);
}
}
printf()
und std::ostream
besteht darin, dass erstere alle Argumente in einem einzigen Aufrufstd::ostream
ausgeben, während für jeden ein separater Aufruf erfolgt <<
. Der Test gibt nur ein Argument und eine neue Zeile aus. Deshalb können Sie den Unterschied nicht erkennen.
printf
könnte eine Menge Anrufe unter der Decke zu Hilfsfunktionen für verschiedene Formatierung Bezeich machen ... das, oder es ist eine monströse monolithische Funktion. Und wieder sollte es wegen Inlining überhaupt keinen Unterschied in der Geschwindigkeit machen.
sprintf
oder fprintf
und stringstream
oder fstream
.
Und ich zitiere :
In hohem Maße sind die Hauptunterschiede die Typensicherheit (cstdio hat sie nicht), die Leistung (die meisten iostreams-Implementierungen sind langsamer als die cstdio-Implementierungen) und die Erweiterbarkeit (iostreams ermöglicht benutzerdefinierte Ausgabeziele und die nahtlose Ausgabe benutzerdefinierter Typen).
Eine ist eine Funktion, die auf stdout druckt. Das andere ist ein Objekt, das operator<<
stdout mehrere Elementfunktionen und Überladungen dieses Drucks bereitstellt. Es gibt noch viele weitere Unterschiede, die ich aufzählen könnte, aber ich bin mir nicht sicher, wonach Sie suchen.
Für mich sind die wirklichen Unterschiede, die mich dazu bringen würden, eher "cout" als "printf" zu wählen, folgende:
1) Der Operator << kann für meine Klassen überladen werden.
2) Der Ausgabestream für cout kann einfach in eine Datei geändert werden: (: Kopieren Einfügen :)
#include <iostream>
#include <fstream>
using namespace std;
int main ()
{
cout << "This is sent to prompt" << endl;
ofstream file;
file.open ("test.txt");
streambuf* sbuf = cout.rdbuf();
cout.rdbuf(file.rdbuf());
cout << "This is sent to file" << endl;
cout.rdbuf(sbuf);
cout << "This is also sent to prompt" << endl;
return 0;
}
3) Ich finde cout besser lesbar, besonders wenn wir viele Parameter haben.
Ein Problem mit cout
ist die Formatierungsoptionen. Das Formatieren der Daten (Genauigkeit, Ausrichtung usw.) printf
ist einfacher.
printf
zu einer Datei wechseln , indem Sie sie durch fprintf
...
Zwei Punkte, die hier nicht anders erwähnt werden und die ich für wichtig halte:
1) cout
trägt viel Gepäck, wenn Sie die STL nicht bereits verwenden. Es fügt Ihrer Objektdatei mehr als doppelt so viel Code hinzu wie printf
. Dies gilt auch für string
, und dies ist der Hauptgrund, warum ich meine eigene String-Bibliothek verwende.
2) cout
verwendet überladene <<
Operatoren, was ich unglücklich finde. Dies kann zu Verwirrung führen, wenn Sie den <<
Operator auch für den vorgesehenen Zweck verwenden (nach links verschieben). Ich persönlich möchte Betreiber nicht für Zwecke überlasten, die tangential zu ihrem Verwendungszweck sind.
Fazit: Ich werde cout
(und string
) verwenden, wenn ich bereits die STL verwende. Ansonsten neige ich dazu, es zu vermeiden.
Bei Grundelementen spielt es wahrscheinlich keine Rolle, welches Sie verwenden. Ich sage, wo es nützlich wird, wenn Sie komplexe Objekte ausgeben möchten.
Wenn Sie beispielsweise eine Klasse haben,
#include <iostream>
#include <cstdlib>
using namespace std;
class Something
{
public:
Something(int x, int y, int z) : a(x), b(y), c(z) { }
int a;
int b;
int c;
friend ostream& operator<<(ostream&, const Something&);
};
ostream& operator<<(ostream& o, const Something& s)
{
o << s.a << ", " << s.b << ", " << s.c;
return o;
}
int main(void)
{
Something s(3, 2, 1);
// output with printf
printf("%i, %i, %i\n", s.a, s.b, s.c);
// output with cout
cout << s << endl;
return 0;
}
Nun, das Obige scheint vielleicht nicht so toll zu sein, aber nehmen wir an, Sie müssen dies an mehreren Stellen in Ihrem Code ausgeben. Angenommen, Sie fügen ein Feld "int d" hinzu. Mit cout müssen Sie es nur an einer Stelle ändern. Mit printf müssten Sie es jedoch möglicherweise an vielen Stellen ändern, und nicht nur das, Sie müssen sich auch daran erinnern, welche ausgegeben werden sollen.
Mit cout können Sie jedoch viel Zeit für die Wartung Ihres Codes sparen und nicht nur, dass Sie sich nicht wirklich um die Ausgabe kümmern müssen, wenn Sie das Objekt "Something" in einer neuen Anwendung wiederverwenden.
Natürlich können Sie "etwas" etwas besser schreiben, um die Wartung aufrechtzuerhalten:
#include <iostream>
#include <cstdlib>
using namespace std;
class Something
{
public:
Something(int x, int y, int z) : a(x), b(y), c(z) { }
int a;
int b;
int c;
friend ostream& operator<<(ostream&, const Something&);
void print() const { printf("%i, %i, %i\n", a, b, c); }
};
ostream& operator<<(ostream& o, const Something& s)
{
o << s.a << ", " << s.b << ", " << s.c;
return o;
}
int main(void)
{
Something s(3, 2, 1);
// Output with printf
s.print(); // Simple as well, isn't it?
// Output with cout
cout << s << endl;
return 0;
}
Und ein etwas erweiterter Test von cout vs. printf, fügte einen Test von 'double' hinzu, wenn jemand mehr Tests durchführen möchte (Visual Studio 2008, Release-Version der ausführbaren Datei):
#include <stdio.h>
#include <iostream>
#include <ctime>
class TimedSection {
char const *d_name;
//timespec d_start;
clock_t d_start;
public:
TimedSection(char const *name) :
d_name(name)
{
//clock_gettime(CLOCK_REALTIME, &d_start);
d_start = clock();
}
~TimedSection() {
clock_t end;
//clock_gettime(CLOCK_REALTIME, &end);
end = clock();
double duration = /*1e3 * (end.tv_sec - d_start.tv_sec) +
1e-6 * (end.tv_nsec - d_start.tv_nsec);
*/
(double) (end - d_start) / CLOCKS_PER_SEC;
std::cerr << d_name << '\t' << std::fixed << duration * 1000.0 << " ms\n";
}
};
int main() {
const int iters = 1000000;
char const *text = "01234567890123456789";
{
TimedSection s("cout with only endl");
for (int i = 0; i < iters; ++i)
std::cout << std::endl;
}
{
TimedSection s("cout with only '\\n'");
for (int i = 0; i < iters; ++i)
std::cout << '\n';
}
{
TimedSection s("printf with only '\\n'");
for (int i = 0; i < iters; ++i)
printf("\n");
}
{
TimedSection s("cout with string constant and endl");
for (int i = 0; i < iters; ++i)
std::cout << "01234567890123456789" << std::endl;
}
{
TimedSection s("cout with string constant and '\\n'");
for (int i = 0; i < iters; ++i)
std::cout << "01234567890123456789\n";
}
{
TimedSection s("printf with string constant and '\\n'");
for (int i = 0; i < iters; ++i)
printf("01234567890123456789\n");
}
{
TimedSection s("cout with some stuff and endl");
for (int i = 0; i < iters; ++i)
std::cout << text << "01234567890123456789" << i << std::endl;
}
{
TimedSection s("cout with some stuff and '\\n'");
for (int i = 0; i < iters; ++i)
std::cout << text << "01234567890123456789" << i << '\n';
}
{
TimedSection s("printf with some stuff and '\\n'");
for (int i = 0; i < iters; ++i)
printf("%s01234567890123456789%i\n", text, i);
}
{
TimedSection s("cout with formatted double (width & precision once)");
std::cout << std::fixed << std::scientific << std::right << std::showpoint;
std::cout.width(8);
for (int i = 0; i < iters; ++i)
std::cout << text << 8.315 << i << '\n';
}
{
TimedSection s("cout with formatted double (width & precision on each call)");
std::cout << std::fixed << std::scientific << std::right << std::showpoint;
for (int i = 0; i < iters; ++i)
{ std::cout.width(8);
std::cout.precision(3);
std::cout << text << 8.315 << i << '\n';
}
}
{
TimedSection s("printf with formatted double");
for (int i = 0; i < iters; ++i)
printf("%8.3f%i\n", 8.315, i);
}
}
Das Ergebnis ist:
cout with only endl 6453.000000 ms
cout with only '\n' 125.000000 ms
printf with only '\n' 156.000000 ms
cout with string constant and endl 6937.000000 ms
cout with string constant and '\n' 1391.000000 ms
printf with string constant and '\n' 3391.000000 ms
cout with some stuff and endl 9672.000000 ms
cout with some stuff and '\n' 7296.000000 ms
printf with some stuff and '\n' 12235.000000 ms
cout with formatted double (width & precision once) 7906.000000 ms
cout with formatted double (width & precision on each call) 9141.000000 ms
printf with formatted double 3312.000000 ms
endl
so viel weniger effizient als '\n'
?
endl
der Puffer geleert wird und \n
nicht, obwohl ich nicht sicher bin, ob dies definitiv der Grund dafür ist.
Ich möchte darauf hinweisen, dass Sie cout
einige interessante Ergebnisse erzielen können, wenn Sie mit Threads in C ++ spielen möchten .
Betrachten Sie diesen Code:
#include <string>
#include <iostream>
#include <thread>
using namespace std;
void task(int taskNum, string msg) {
for (int i = 0; i < 5; ++i) {
cout << "#" << taskNum << ": " << msg << endl;
}
}
int main() {
thread t1(task, 1, "AAA");
thread t2(task, 2, "BBB");
t1.join();
t2.join();
return 0;
}
// g++ ./thread.cpp -o thread.out -ansi -pedantic -pthread -std=c++0x
Jetzt wird die Ausgabe komplett gemischt. Es kann auch zu unterschiedlichen Ergebnissen führen. Versuchen Sie es mehrmals:
##12:: ABABAB
##12:: ABABAB
##12:: ABABAB
##12:: ABABAB
##12:: ABABAB
Sie können verwenden printf
, um es richtig zu machen, oder Sie können verwenden mutex
.
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
Habe Spaß!
thread
s machen die Ausgabe nicht verrückt. Ich habe gerade beide reproduziert und gefunden xyz
und ABC
in der Ausgabe. Es wurde nicht s / w ABC
als zerfetzt ABABAB
.
cout
mit Threads funktioniert, aber ich weiß mit Sicherheit, dass der Code, den Sie anzeigen, nicht der ist, mit dem Sie diese Ausgaben erhalten haben. Ihr Code übergibt die Zeichenfolge "ABC"
für Thread 1 und "xyz"
für Thread 2, aber Ihre Ausgabe zeigt AAA
und BBB
. Bitte beheben Sie es, weil es im Moment verwirrend ist.
cout<< "Hello";
printf("%s", "Hello");
Beide werden zum Drucken von Werten verwendet. Sie haben eine völlig andere Syntax. C ++ hat beides, C hat nur printf.
Ich möchte sagen, dass der Mangel an Erweiterbarkeit printf
nicht ganz stimmt:
In C ist es wahr. Aber in C gibt es keine wirklichen Klassen.
In C ++ ist es möglich, den Cast-Operator zu überladen. Überladen Sie also einen char*
Operator und verwenden Sie printf
Folgendes:
Foo bar;
...;
printf("%s",bar);
kann möglich sein, wenn Foo den guten Bediener überlastet. Oder wenn Sie eine gute Methode gemacht haben. Kurz gesagt, printf
ist so erweiterbar wie cout
für mich.
Technische Argumente, die ich für C ++ - Streams sehen kann (im Allgemeinen ... nicht nur cout.), Sind:
Typensicherheit. (Und übrigens, wenn ich eine einzelne drucken möchte, die '\n'
ich benutze putchar('\n')
... werde ich keine Atombombe verwenden, um ein Insekt zu töten.)
Einfacher zu lernen. (keine "komplizierten" Parameter zu lernen, nur zu verwenden <<
und >>
Operatoren)
Arbeite nativ mit std::string
(denn printf
es gibt std::string::c_str()
, aber für scanf
?)
Denn printf
ich sehe:
Einfachere oder zumindest kürzere (in Bezug auf die geschriebenen Zeichen) komplexe Formatierung. Für mich weitaus lesbarer (Geschmackssache, denke ich).
Bessere Kontrolle darüber, was die Funktion gemacht hat (Geben Sie zurück, wie viele Zeichen geschrieben wurden, und es gibt den %n
Formatierer: "Nichts gedruckt. Das Argument muss ein Zeiger auf ein vorzeichenbehaftetes int sein, in dem die Anzahl der bisher geschriebenen Zeichen gespeichert ist." (Von printf - C ++ Referenz )
Bessere Debugging-Möglichkeiten. Aus demselben Grund wie das letzte Argument.
Meine persönlichen Vorlieben beziehen sich auf printf
(und scanf
) Funktionen, hauptsächlich weil ich kurze Zeilen liebe und weil ich nicht denke, dass Schreibprobleme beim Drucken von Text wirklich schwer zu vermeiden sind. Das einzige, was ich mit Funktionen im C-Stil bedaure, ist, dass std::string
es nicht unterstützt wird. Wir müssen ein durchlaufen, char*
bevor wir es geben printf
(mit dem, std::string::c_str()
wenn wir lesen wollen, aber wie schreiben?)
char*
wird nicht verwendet.
char*
und wie lange das Leben und die Gefahren von benutzerdefinierten implizite Besetzungen.
Weitere Unterschiede: "printf" gibt einen ganzzahligen Wert zurück (der der Anzahl der gedruckten Zeichen entspricht) und "cout" gibt nichts zurück
Und.
cout << "y = " << 7;
ist nicht atomar.
printf("%s = %d", "y", 7);
ist atomar.
cout führt typechecking durch, printf nicht.
Es gibt kein iostream-Äquivalent von "% d"
cout
gibt nichts zurück, weil es ein Objekt ist, keine Funktion. operator<<
gibt etwas zurück (normalerweise der linke Operand, aber ein falscher Wert, wenn ein Fehler vorliegt). Und in welchem Sinne ist der printf
Ruf "atomar"?
printf("%s\n",7);
%s
ist?
printf
% s- Argument muss einen gültigen Zeiger auf eine nullterminierte Zeichenfolge haben. Der Speicherbereich '7' (ein Zeiger) ist normalerweise nicht gültig; Ein Segmentierungsfehler könnte Glück haben. Auf einigen Systemen druckt '7' möglicherweise viel Müll auf eine Konsole, und Sie müssten ihn einen Tag lang ansehen, bevor das Programm beendet wird. Mit anderen Worten, das ist eine schlechte Sache printf
. Statische Analysewerkzeuge können viele dieser Probleme lösen.
printf
keine Schreibprüfung durchgeführt wird, habe ich noch nie einen Compiler verwendet, der mich nicht vor Tippfehlern gewarnt hat mit printf
...
TL; DR: Machen Sie immer Ihre eigenen Nachforschungen hinsichtlich der Größe , Leistung , Lesbarkeit und Codierungszeit des generierten Maschinencodes , bevor Sie zufälligen Kommentaren online vertrauen, einschließlich dieser.
Ich bin kein Experte. Ich habe gerade zwei Mitarbeiter belauscht, die darüber sprachen, wie wir die Verwendung von C ++ in eingebetteten Systemen aufgrund von Leistungsproblemen vermeiden sollten. Interessanterweise habe ich einen Benchmark durchgeführt, der auf einer echten Projektaufgabe basiert.
In dieser Aufgabe mussten wir eine Konfiguration in den RAM schreiben. Etwas wie:
Kaffee = heißer
Zucker = keine
Milch = Brust
Mac = AA: BB: CC: DD: EE: FF
Hier sind meine Benchmark-Programme (Ja, ich weiß, dass OP nach printf () und nicht nach fprintf () gefragt hat. Versuchen Sie, die Essenz zu erfassen, und der Link von OP verweist übrigens trotzdem auf fprintf ().)
C-Programm:
char coffee[10], sugar[10], milk[10];
unsigned char mac[6];
/* Initialize those things here. */
FILE * f = fopen("a.txt", "wt");
fprintf(f, "coffee=%s\nsugar=%s\nmilk=%s\nmac=%02X:%02X:%02X:%02X:%02X:%02X\n", coffee, sugar, milk, mac[0], mac[1],mac[2],mac[3],mac[4],mac[5]);
fclose(f);
C ++ - Programm:
//Everything else is identical except:
std::ofstream f("a.txt", std::ios::out);
f << "coffee=" << coffee << "\n";
f << "sugar=" << sugar << "\n";
f << "milk=" << milk << "\n";
f << "mac=" << (int)mac[0] << ":"
<< (int)mac[1] << ":"
<< (int)mac[2] << ":"
<< (int)mac[3] << ":"
<< (int)mac[4] << ":"
<< (int)mac[5] << endl;
f.close();
Ich habe mein Bestes getan, um sie zu polieren, bevor ich sie beide 100.000 Mal geloopt habe. Hier sind die Ergebnisse:
C-Programm:
real 0m 8.01s
user 0m 2.37s
sys 0m 5.58s
C ++ - Programm:
real 0m 6.07s
user 0m 3.18s
sys 0m 2.84s
Objektdateigröße:
C - 2,092 bytes
C++ - 3,272 bytes
Fazit: Auf meiner ganz bestimmten Plattform mit einem ganz bestimmten Prozessor , auf dem eine ganz bestimmte Version des Linux-Kernels ausgeführt wird, um ein Programm auszuführen, das mit einer ganz bestimmten Version von GCC kompiliert wurde , um eine ganz bestimmte Aufgabe zu erfüllen , würde ich sagen Der C ++ - Ansatz ist besser geeignet, da er erheblich schneller ausgeführt wird und eine viel bessere Lesbarkeit bietet. Auf der anderen Seite bedeutet C einen geringen Platzbedarf, was meiner Meinung nach fast nichts bedeutet, da die Programmgröße nicht unser Anliegen ist.
Denken Sie daran, YMMV.
Ich bin kein Programmierer, aber ich war ein Ingenieur für menschliche Faktoren. Ich bin der Meinung, dass eine Programmiersprache leicht zu erlernen, zu verstehen und zu verwenden sein sollte, und dies erfordert eine einfache und konsistente Sprachstruktur. Obwohl alle Sprachen symbolisch und daher im Kern willkürlich sind, gibt es Konventionen, deren Befolgung das Erlernen und Verwenden der Sprache erleichtert.
Es gibt eine Vielzahl von Funktionen in C ++ und anderen Sprachen, die als Funktion (Parameter) geschrieben wurden, eine Syntax, die ursprünglich für funktionale Beziehungen in der Mathematik in der Zeit vor dem Computer verwendet wurde. printf()
folgt dieser Syntax und wenn die Autoren von C ++ eine logisch andere Methode zum Lesen und Schreiben von Dateien erstellen wollten, hätten sie einfach eine andere Funktion mit einer ähnlichen Syntax erstellen können.
In Python können wir natürlich mit der ebenfalls ziemlich standardmäßigen object.method
Syntax drucken , dh variablename.print, da Variablen Objekte sind, in C ++ jedoch nicht.
Ich mag die Cout-Syntax nicht, weil der Operator << keine Regeln befolgt. Es ist eine Methode oder Funktion, dh es nimmt einen Parameter und macht etwas damit. Es ist jedoch so geschrieben, als wäre es ein mathematischer Vergleichsoperator. Dies ist aus menschlicher Sicht ein schlechter Ansatz.
printf
ist eine Funktion, während cout
es sich um eine Variable handelt.
printf
ist eine Funktion, aber printf()
ist ein Funktionsaufruf =)