Je suis surpris que tout le monde dans cette question affirme que std::cout
est bien meilleur que printf
même si la question ne demandait que des différences. Maintenant, il y a une différence - std::cout
est C++, et printf
est en C (toutefois, vous pouvez l'utiliser en C++, tout comme casi tout autre élément de C). Maintenant, je vais être honnête ici ; les deux printf
y std::cout
ont leurs avantages.
Différences réelles
Extensibilité
std::cout
est extensible. Je sais que les gens vont dire que printf
est également extensible, mais une telle extension n'est pas mentionnée dans la norme C (il faudrait donc utiliser des fonctionnalités non standard - mais il n'existe même pas de fonctionnalité non standard commune), et ces extensions sont d'une seule lettre (il est donc facile d'entrer en conflit avec un format déjà existant).
Contrairement à printf
, std::cout
dépend entièrement de la surcharge des opérateurs, de sorte qu'il n'y a pas de problème avec les formats personnalisés - il suffit de définir une sous-routine prenant le format std::ostream
comme premier argument et votre type comme second. En tant que tel, il n'y a pas de problème d'espace de noms - tant que vous avez une classe (qui n'est pas limitée à un caractère), vous pouvez avoir des fonctions std::ostream
surcharge pour elle.
Cependant, je doute que beaucoup de personnes veuillent prolonger ostream
(pour être honnête, j'ai rarement vu de telles extensions, même si elles sont faciles à réaliser). Cependant, elle est là si vous en avez besoin.
Syntaxe
Comme on peut facilement le constater, les deux printf
y std::cout
utilisent une syntaxe différente. printf
utilise une syntaxe de fonction standard utilisant des chaînes de motifs et des listes d'arguments de longueur variable. En fait, printf
Il y a une raison pour laquelle C les a - printf
sont trop complexes pour être utilisables sans eux. Cependant, std::cout
utilise une API différente - l operator <<
API qui se renvoie elle-même.
En général, cela signifie que la version C sera plus courte, mais dans la plupart des cas, cela n'aura aucune importance. La différence est perceptible lorsque vous imprimez de nombreux arguments. Si vous devez écrire quelque chose comme Error 2: File not found.
En supposant que le numéro de l'erreur et sa description sont des caractères de remplissage, le code ressemblerait à ceci. Les deux exemples fonctionnent de manière identique (enfin, en quelque sorte, std::endl
vide effectivement le tampon).
printf("Error %d: %s.\n", id, errors[id]);
std::cout << "Error " << id << ": " << errors[id] << "." << std::endl;
Bien que cela ne semble pas trop fou (c'est juste deux fois plus long), les choses deviennent plus folles lorsque vous formatez réellement les arguments, au lieu de simplement les imprimer. Par exemple, l'impression de quelque chose comme 0x0424
est juste fou. Ceci est causé par std::cout
mélangeant les valeurs d'état et les valeurs réelles. Je n'ai jamais vu un langage où quelque chose comme std::setfill
serait un type (autre que C++, bien sûr). printf
sépare clairement les arguments et le type réel. Je préférerais vraiment maintenir le printf
(même s'il a l'air un peu cryptique) par rapport aux iostream
version (car elle contient trop de bruit).
printf("0x%04x\n", 0x424);
std::cout << "0x" << std::hex << std::setfill('0') << std::setw(4) << 0x424 << std::endl;
Traduction
C'est là que le véritable avantage de printf
mensonges. Le site printf
La chaîne de format est bien... une chaîne. Cela le rend vraiment facile à traduire, comparé à operator <<
l'abus de iostream
. En supposant que le gettext()
traduit la fonction, et vous voulez montrer Error 2: File not found.
le code pour obtenir la traduction de la chaîne de format présentée précédemment ressemblerait à ceci :
printf(gettext("Error %d: %s.\n"), id, errors[id]);
Maintenant, supposons que nous traduisions en Fictionish, où le numéro d'erreur se trouve après la description. La chaîne traduite ressemblerait à %2$s oru %1$d.\n
. Maintenant, comment le faire en C++ ? Eh bien, je n'en ai aucune idée. Je suppose que vous pouvez faire des faux iostream
qui construit printf
que vous pouvez passer à gettext
ou autre, à des fins de traduction. Bien sûr, $
n'est pas la norme C, mais elle est si courante qu'elle peut être utilisée sans risque à mon avis.
Ne pas avoir à se souvenir de la syntaxe spécifique des types d'entiers.
Le C a beaucoup de types d'entiers, et le C++ aussi. std::cout
gère tous les types pour vous, tandis que printf
requiert une syntaxe spécifique dépendant d'un type d'entier (il existe des types non entiers, mais le seul type non entier que vous utiliserez en pratique avec printf
es const char *
(chaîne C, peut être obtenue en utilisant to_c
méthode de std::string
)). Par exemple, pour imprimer size_t
vous devez utiliser %zu
alors que int64_t
il faudra utiliser %"PRId64"
. Les tableaux sont disponibles à l'adresse suivante http://en.cppreference.com/w/cpp/io/c/fprintf y http://en.cppreference.com/w/cpp/types/integer .
Vous ne pouvez pas imprimer l'octet NUL, \0
Parce que printf
utilise des chaînes de caractères C par opposition aux chaînes de caractères C++, il ne peut pas imprimer l'octet NUL sans astuces spécifiques. Dans certains cas, il est possible d'utiliser %c
con '\0'
comme un argument, bien que ce soit clairement un hack.
Des différences dont personne ne se soucie
Performance
Mise à jour : Il s'avère que iostream
est si lent qu'il est généralement plus lent que votre disque dur (si vous redirigez votre programme vers un fichier). Désactiver la synchronisation avec stdio
peut aider, si vous avez besoin de sortir beaucoup de données. Si les performances sont un réel souci (par rapport à l'écriture de plusieurs lignes dans STDOUT), utilisez simplement printf
.
Tout le monde pense se soucier des performances, mais personne ne se soucie de les mesurer. Ma réponse est que les E/S sont de toute façon un goulot d'étranglement, peu importe si vous utilisez printf
o iostream
. Je pense que printf
pourrait être plus rapide d'après un rapide coup d'œil dans l'assemblage (compilé avec clang en utilisant la fonction -O3
option du compilateur). En supposant que mon exemple d'erreur, printf
fait beaucoup moins d'appels que l'exemple cout
exemple. C'est int main
con 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:
Vous pouvez facilement remarquer que deux chaînes de caractères, et 2
(nombre) sont poussés comme printf
arguments. C'est à peu près tout ; il n'y a rien d'autre. À titre de comparaison, voici iostream
compilé en assemblage. Non, il n'y a pas d'inlining ; chaque simple operator <<
signifie un autre appel avec un autre ensemble d'arguments.
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:
Cependant, pour être honnête, cela ne signifie rien, car les E/S sont de toute façon le goulot d'étranglement. Je voulais juste montrer que iostream
n'est pas plus rapide parce qu'elle est "sûre en type". La plupart des implémentations C mettent en œuvre printf
en utilisant le goto calculé, donc le printf
est aussi rapide qu'il peut l'être, même sans que le compilateur soit conscient de printf
(non pas qu'ils ne le soient pas - certains compilateurs peuvent optimiser printf
dans certains cas - chaîne constante se terminant par \n
est généralement optimisé pour puts
).
Héritage
Je ne sais pas pourquoi vous voudriez hériter ostream
mais je m'en fiche. C'est possible avec FILE
aussi.
class MyFile : public FILE {}
Sécurité de type
Il est vrai que les listes d'arguments de longueur variable n'ont pas de sécurité, mais cela n'a pas d'importance, car les compilateurs C les plus courants peuvent détecter les problèmes avec les listes d'arguments de longueur variable. printf
chaîne de format si vous activez les avertissements. En fait, Clang peut le faire sans activer les avertissements.
$ 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);
^