Antworten
Zu viele Anzeigen?Ich bin überrascht, dass jeder in dieser Frage behauptet, dass std::cout
ist viel besser als printf
auch wenn in der Frage nur nach Unterschieden gefragt wurde. Nun, es gibt einen Unterschied - std::cout
ist C++, und printf
ist C (Sie können es aber auch in C++ verwenden, genau wie fast alles andere von C). Ich will hier ehrlich sein: Beide printf
y std::cout
haben ihre Vorteile.
Echte Unterschiede
Erweiterbarkeit
std::cout
ist erweiterbar. Ich weiß, dass die Leute sagen werden, dass printf
ist ebenfalls erweiterbar, aber eine solche Erweiterung wird im C-Standard nicht erwähnt (man müsste also Nicht-Standard-Funktionen verwenden - aber es gibt nicht einmal gemeinsame Nicht-Standard-Funktionen), und solche Erweiterungen bestehen aus einem Buchstaben (so dass es leicht zu Konflikten mit einem bereits vorhandenen Format kommt).
Anders als printf
, std::cout
hängt vollständig von der Überladung von Operatoren ab, so dass es keine Probleme mit benutzerdefinierten Formaten gibt - Sie müssen lediglich ein Unterprogramm definieren, das std::ostream
als erstes Argument und Ihren Typ als zweites. Daher gibt es keine Probleme mit dem Namensraum - solange Sie eine Klasse haben (die nicht auf ein Zeichen beschränkt ist), können Sie eine funktionierende std::ostream
Überlastung für sie.
Ich bezweifle jedoch, dass viele Menschen die ostream
(um ehrlich zu sein, habe ich solche Erweiterungen selten gesehen, auch wenn sie leicht zu machen sind). Wie auch immer, es ist hier, wenn Sie es brauchen.
Syntax
Wie unschwer zu erkennen ist, sind beide printf
y std::cout
eine andere Syntax verwenden. printf
verwendet eine Standardfunktionssyntax mit Musterzeichenfolgen und Argumentlisten variabler Länge. Eigentlich, printf
ist ein Grund, warum C sie hat - printf
Formate sind zu komplex, um ohne sie verwendet werden zu können. Allerdings, std::cout
verwendet eine andere API - die operator <<
API, die sich selbst zurückgibt.
Im Allgemeinen bedeutet das, dass die C-Version kürzer ist, aber in den meisten Fällen spielt das keine Rolle. Der Unterschied wird deutlich, wenn Sie viele Argumente ausgeben. Wenn Sie etwas schreiben müssen wie Error 2: File not found.
Wenn man davon ausgeht, dass die Fehlernummer und die Beschreibung Platzhalter sind, würde der Code wie folgt aussehen. Beide Beispiele identisch arbeiten (naja, irgendwie schon, std::endl
den Puffer tatsächlich leert).
printf("Error %d: %s.\n", id, errors[id]);
std::cout << "Error " << id << ": " << errors[id] << "." << std::endl;
Während dies nicht allzu verrückt erscheint (es ist nur zwei Mal länger), wird es noch verrückter, wenn man Argumente tatsächlich formatiert, anstatt sie nur auszudrucken. Zum Beispiel wird das Drucken von etwas wie 0x0424
ist einfach verrückt. Dies wird verursacht durch std::cout
Vermischung von Zustand und tatsächlichen Werten. Ich habe noch nie eine Sprache gesehen, in der etwas wie std::setfill
wäre ein Typ (natürlich ein anderer als C++). printf
trennt klar zwischen Argumenten und dem eigentlichen Typ. Ich würde es wirklich vorziehen, die printf
Version davon (auch wenn sie etwas kryptisch aussieht) im Vergleich zu 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;
Übersetzung
Hier liegt der eigentliche Vorteil von printf
liegt. Die printf
format string ist nun einmal... eine Zeichenkette. Das macht es wirklich einfach zu übersetzen, verglichen mit operator <<
Missbrauch von iostream
. Unter der Annahme, dass die gettext()
Funktion übersetzt, und Sie wollen zeigen Error 2: File not found.
würde der Code für die Übersetzung der zuvor gezeigten Formatzeichenfolge wie folgt aussehen:
printf(gettext("Error %d: %s.\n"), id, errors[id]);
Nehmen wir nun an, wir übersetzen ins Fiktive, wo die Fehlernummer nach der Beschreibung steht. Die übersetzte Zeichenfolge würde wie folgt aussehen %2$s oru %1$d.\n
. Wie macht man das nun in C++? Nun, ich habe keine Ahnung. Ich schätze, man kann gefälschte iostream
die Konstruktionen printf
die Sie an gettext
oder so ähnlich, für die Zwecke der Übersetzung. Natürlich, $
ist kein C-Standard, aber er ist so weit verbreitet, dass er meiner Meinung nach sicher verwendet werden kann.
Sie müssen sich nicht an die spezifische Syntax von Ganzzahlen erinnern bzw. diese nachschlagen.
C hat viele Integer-Typen, ebenso wie C++. std::cout
bearbeitet alle Typen für Sie, während printf
erfordert eine spezielle Syntax, die von einem Integer-Typ abhängt (es gibt auch nicht-ganzzahlige Typen, aber der einzige nicht-ganzzahlige Typ, den Sie in der Praxis mit printf
es const char *
(C-String), kann man erhalten durch to_c
Methode der std::string
)). Zum Beispiel, um zu drucken size_t
müssen Sie Folgendes verwenden %zu
, während int64_t
erfordert die Verwendung von %"PRId64"
. Die Tabellen sind verfügbar unter http://en.cppreference.com/w/cpp/io/c/fprintf y http://en.cppreference.com/w/cpp/types/integer .
Sie können das NUL-Byte nicht ausdrucken, \0
Denn printf
C-Strings im Gegensatz zu C++-Strings verwendet, kann es ohne spezielle Tricks kein NUL-Byte ausgeben. In bestimmten Fällen ist es möglich, mit %c
avec '\0'
als Argument, obwohl das eindeutig ein Scherz ist.
Unterschiede, für die sich niemand interessiert
Leistung
Aktualisierung: Es hat sich herausgestellt, dass iostream
ist so langsam, dass es normalerweise langsamer ist als Ihre Festplatte (wenn Sie Ihr Programm in eine Datei umleiten). Deaktivieren der Synchronisierung mit stdio
kann helfen, 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
.
Alle glauben, dass ihnen die Leistung wichtig ist, aber niemand macht sich die Mühe, sie zu messen. Meine Antwort ist, dass E/A sowieso der Flaschenhals ist, egal ob man printf
o iostream
. Ich denke, dass printf
könnte schneller sein, wie ein kurzer Blick in die Assemblierung zeigt (kompiliert mit Clang unter Verwendung der -O3
Compiler-Option). Angenommen, mein Fehlerbeispiel, printf
Beispiel macht viel weniger Aufrufe als das cout
Beispiel. Dies ist int main
avec 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 Zeichenketten, und 2
(Zahl) werden geschoben als printf
Argumente. Das war's; es gibt nichts anderes. Zum Vergleich: Dies ist iostream
zu einer Baugruppe kompiliert. Nein, es gibt kein Inlining; jede 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 Flaschenhals ist. Ich wollte nur zeigen, dass iostream
ist nicht schneller, weil es "typsicher" ist. Die meisten C-Implementierungen implementieren printf
Formate unter Verwendung des berechneten goto, so dass die printf
ist so schnell wie möglich, auch wenn der Compiler nicht weiß, dass printf
(nicht, dass sie es nicht wären - einige Compiler können die printf
in bestimmten Fällen - konstante Zeichenfolge mit der Endung \n
ist in der Regel optimiert auf puts
).
Vererbung
Ich weiß nicht, warum Sie erben wollen. ostream
aber das ist mir egal. Es ist möglich mit FILE
auch.
class MyFile : public FILE {}
Typ Sicherheit
Es stimmt, dass Argumentlisten mit variabler Länge keine Sicherheit bieten, aber das spielt keine Rolle, da gängige C-Compiler Probleme mit printf
Formatstring, wenn Sie Warnungen aktivieren. Tatsächlich kann Clang dies auch ohne die Aktivierung von Warnungen tun.
$ 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);
^
Von der C++ FAQ :
[15.1] Warum sollte ich die
<iostream>
anstelle der traditionellen<cstdio>
?Erhöhen Sie die Typsicherheit, reduzieren Sie Fehler, ermöglichen Sie Erweiterbarkeit und bieten Sie Vererbbarkeit.
printf()
ist wohl nicht gebrochen, undscanf()
ist trotz seiner Fehleranfälligkeit vielleicht lebbar, aber beide sind im Hinblick auf die Möglichkeiten von C++ I/O begrenzt. C++ E/A (mit<<
y>>
) ist, bezogen auf C (unter Verwendung vonprintf()
yscanf()
):
- Mehr Typsicherheit: Mit
<iostream>
ist der Typ des E/A-Objekts statisch durch den Compiler bekannt. In Gegensatz dazu,<cstdio>
verwendet "%"-Felder, um die Typen dynamisch zu ermitteln.- Weniger fehleranfällig: Mit
<iostream>
gibt es keine redundanten "%"-Zeichen, die mit den tatsächlichen mit den tatsächlichen Objekten übereinstimmen müssen, die I/O'd sind. Die Beseitigung der Redundanz beseitigt eine Klasse von Fehlern.- Erweiterbar: Die C++
<iostream>
Mechanismus ermöglicht neue benutzerdefinierte E/A-Typen, ohne dass der bestehende bestehenden Code. Stellen Sie sich das Chaos vor, wenn alle gleichzeitig neue neue inkompatible "%"-Felder zuprintf()
yscanf()
?!- Vererbbar: Die C++
<iostream>
Mechanismus ist aus echten Klassen aufgebaut wie z.B.std::ostream
etstd::istream
. Anders als<cstdio>
'sFILE*
sind dies echte Klassen und daher vererbbar. Das bedeutet, Sie können andere benutzerdefinierte Dinge haben, die aussehen und sich wie Streams verhalten, die jedoch tun, was auch immer für seltsame und wunderbare Dinge tun, die Sie wollen. Sie können automatisch die Zillionen von Zeilen mit E/A-Code, die von Benutzern geschrieben wurden, die Sie nicht die Sie nicht einmal kennen, und sie müssen auch nicht über Ihre "erweiterte Stream"-Klasse wissen Klasse.
Andererseits, printf
ist deutlich schneller, was es rechtfertigen kann, es stattdessen zu verwenden cout
en très spezifische und begrenzte Fälle. Erstellen Sie immer zuerst ein Profil. (Siehe z. B., http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /)
Es wird oft behauptet, dass printf
ist viel schneller. Das ist weitgehend ein Mythos. Ich habe es gerade getestet, mit den folgenden Ergebnissen:
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 wollen, verwenden Sie printf
; sonst, cout
ist fast genauso schnell, wenn nicht sogar schneller. Weitere Einzelheiten finden Sie unter mein Blog .
Um das klarzustellen, ich will damit nicht sagen, dass iostream
s sind immer besser als printf
Ich will damit nur sagen, dass Sie eine fundierte Entscheidung auf der Grundlage realer Daten treffen sollten und nicht eine wilde Vermutung auf der Grundlage einer allgemeinen, irreführenden Annahme.
Update: Hier ist der vollständige Code, den ich zum Testen verwendet habe. Kompiliert mit g++
ohne zusätzliche Optionen (abgesehen von -lrt
für den Zeitplan).
#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);
}
}
Und ich Zitat :
Die wichtigsten Unterschiede sind die Typsicherheit (cstdio hat sie nicht), Leistung (die meisten iostreams-Implementierungen sind langsamer als die von cstdio) und Erweiterbarkeit (iostreams erlaubt benutzerdefinierte Ausgabeziele und nahtlose Ausgabe von benutzerdefinierten Typen).
- See previous answers
- Weitere Antworten anzeigen