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 C (toutefois, vous pouvez l'utiliser en C++, tout comme presque autre chose de C). Maintenant, je vais être honnête ici; printf
et std::cout
ont tous deux des avantages.
std::cout
est extensible. Je sais que les gens diront que printf
est extensible également, mais cette extension n'est pas mentionnée dans la norme C (vous devrez donc utiliser des fonctionnalités non standard, mais il n'existe même pas de fonctionnalité non standard courante), et ces extensions ont une 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 de l'opérateur. Il n'y a donc aucun problème avec les formats personnalisés. Vous définissez simplement un sous-programme prenant std::ostream
comme premier argument et votre type comme second. En tant que tel, il n’ya 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 la surcharge std::ostream
pour cette classe.
Cependant, je doute que beaucoup de gens veuillent prolonger ostream
(pour être honnête, j'ai rarement vu de telles extensions, même si elles sont faciles à réaliser). Cependant, c'est ici si vous en avez besoin.
Comme on peut facilement le constater, printf
et std::cout
utilisent une syntaxe différente. printf
utilise la syntaxe de fonction standard à l'aide de chaînes de modèle et de listes d'arguments de longueur variable. En fait, printf
est une des raisons pour lesquelles C les a - printf
les formats sont trop complexes pour être utilisables sans eux. Cependant, std::cout
utilise une API différente - l'API operator <<
qui se retourne.
En règle générale, cela signifie que la version C sera plus courte, mais dans la plupart des cas, cela n'aura pas d'importance. La différence est notable lorsque vous imprimez de nombreux arguments. Si vous devez écrire quelque chose comme Error 2: File not found.
, en supposant que le numéro d'erreur, et que sa description est un espace réservé, le code ressemblerait à ceci. Les deux exemples fonctionnent à l'identique (enfin, en quelque sorte, std::endl
vide 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 (ce n'est que deux fois plus long), les choses deviennent de plus en plus folles lorsque vous formatez des arguments, au lieu de les imprimer. Par exemple, imprimer quelque chose comme 0x0424
est simplement fou. Ceci est dû à std::cout
l'état de mélange et aux 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 conserver la version printf
(même si elle a l'air assez cryptique) par rapport à la version iostream
(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;
C’est là que réside le véritable avantage de printf
. La chaîne de format printf
est bien ... une chaîne. Cela facilite vraiment la traduction, comparé à operator <<
abus de iostream
. En supposant que la fonction gettext()
traduise et que vous souhaitiez afficher Error 2: File not found.
, le code permettant d'obtenir la traduction de la chaîne de format précédemment affichée ressemblerait à ceci:
printf(gettext("Error %d: %s.\n"), id, errors[id]);
Supposons maintenant que nous traduisons en Fictionish, où le numéro d'erreur est 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 créer de faux iostream
qui construisent printf
et que vous pouvez passer à gettext
, ou quelque chose d’autre, à des fins de traduction. Bien sûr, $
n'est pas standard, mais il est si courant que son utilisation est sûre, à mon avis.
C a beaucoup de types entiers, tout comme C++. std::cout
gère tous les types pour vous, tandis que printf
requiert une syntaxe spécifique dépendant d'un type entier (il existe des types non entiers, mais le seul type non entier que vous utiliserez dans la pratique avec printf
is const char *
(La chaîne C, peut être obtenue en utilisant la méthode to_c
de std::string
)). Par exemple, pour imprimer size_t
, vous devez utiliser %zd
, tandis que int64_t
nécessitera l’utilisation de %"PRId64"
. Les tableaux sont disponibles sur le site http://en.cppreference.com/w/cpp/io/c/fprintf et http://en.cppreference.com/w/cpp/ types/entier .
\0
Comme printf
utilise des chaînes C plutôt que C++, il ne peut pas imprimer d'octet NUL sans astuces spécifiques. Dans certains cas, il est possible d'utiliser %c
avec '\0'
comme argument, bien que ce soit clairement un hack.
Mise à jour: Il se trouve 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 être utile si vous devez générer beaucoup de données. Si la performance est un réel problème (par opposition à l'écriture de plusieurs lignes dans STDOUT), utilisez simplement printf
.
Tout le monde pense qu'ils se soucient de la performance, mais personne ne se soucie de la mesurer. Ma réponse est que les E/S sont un goulot d'étranglement de toute façon, que vous utilisiez printf
ou iostream
. Je pense que printf
pourrait être plus rapide à partir d’un coup d’œil rapide dans Assembly (compilé avec clang à l’aide de l’option -O3
du compilateur). En supposant mon exemple d'erreur, l'exemple printf
effectue beaucoup moins d'appels que l'exemple cout
. C'est 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:
Vous pouvez facilement remarquer que deux chaînes et 2
(nombre) sont poussés en tant qu'arguments printf
. C'est à peu près ça; il n'y a rien d'autre. À titre de comparaison, il s'agit de iostream
compilé pour Assembly. Non, il n'y a pas d'inline. chaque appel 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 veut rien dire, car les entrées/sorties constituent de toute façon le goulot d'étranglement. Je voulais juste montrer que iostream
n'est pas plus rapide parce que c'est "type safe". La plupart des implémentations C implémentent les formats printf
à l'aide de la méthode goto calculée, de sorte que la printf
soit aussi rapide que possible, même sans que le compilateur soit au courant de printf
(pas qu'ils ne le soient pas - certains compilateurs peut optimiser printf
dans certains cas - la chaîne constante se terminant par \n
est généralement optimisée sur puts
).
Je ne sais pas pourquoi vous voudriez hériter de ostream
, mais je m'en fiche. C'est possible avec FILE
aussi.
class MyFile : public FILE {}
Certes, les listes d'arguments de longueur variable n'ont aucune sécurité, mais cela n'a pas d'importance, car les compilateurs C les plus répandus peuvent détecter les problèmes liés à la chaîne de format printf
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);
^
De la C++ FAQ :
[15.1] Pourquoi devrais-je utiliser
<iostream>
au lieu du traditionnel<cstdio>
?Augmentez la sécurité des types, réduisez les erreurs, autorisez l'extensibilité et offrez une héritabilité.
printf()
n'est pas cassé, etscanf()
est peut-être habitable même s'il est sujet à des erreurs, mais les deux sont limités par rapport à ce que les E/S C++ peuvent faire. Les E/S C++ (en utilisant<<
et>>
) sont, par rapport à C (en utilisantprintf()
etscanf()
):
- Plus sûr pour le type: Avec
<iostream>
, le type d'objet étant I/O'd est connu de manière statique par le compilateur. En revanche,<cstdio>
utilise des champs "%" pour déterminer les types de manière dynamique.- Moins sujet aux erreurs: avec
<iostream>
, il n'y a pas de jetons "%" redondants qui doivent être cohérents avec les objets réels étant I/O'd. La suppression de la redondance supprime une classe d'erreurs.- Extensible: le mécanisme C++
<iostream>
permet aux nouveaux types définis par l'utilisateur d'être I/O'd sans casser le code existant. Imaginez le chaos si tout le monde ajoutait simultanément de nouveaux champs "%" incompatibles àprintf()
etscanf()
?!- Inheritable: le mécanisme C++
<iostream>
est construit à partir de classes réelles telles questd::ostream
etstd::istream
. Contrairement à<cstdio>
'sFILE*
, ce sont de vraies classes et sont donc héritables. Cela signifie que vous pouvez avoir d'autres choses définies par l'utilisateur qui ressemblent et agissent comme des flux, mais qui font tout ce que vous voulez d'étrange et de merveilleux. Vous pouvez automatiquement utiliser les zillions de lignes de code d'E/S écrites par des utilisateurs que vous ne connaissez même pas, et ils n'ont pas besoin de connaître votre classe "extended stream".
Par contre, printf
est nettement plus rapide, ce qui peut justifier son utilisation de préférence à cout
dans des cas très spécifiques et limités. Toujours profil en premier. (Voir, par exemple, http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /)
Les gens prétendent souvent que printf
est beaucoup plus rapide. C'est en grande partie un mythe. Je viens de le tester, avec les résultats suivants:
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
Conclusion: si vous ne voulez que les nouvelles lignes, utilisez printf
; sinon, cout
est presque aussi rapide, voire plus rapide. Plus de détails peuvent être trouvés sur mon blog .
Pour être clair, je n'essaie pas de dire que iostream
s sont toujours meilleurs que printf
; J'essaie simplement de dire que vous devez prendre une décision éclairée basée sur des données réelles, et non sur une hypothèse imprudente basée sur une hypothèse courante et trompeuse.
Mise à jour: Voici le code complet que j'ai utilisé pour les tests. Compilé avec g++
sans aucune option supplémentaire (à part -lrt
pour le minutage).
#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);
}
}
Et je citation :
En termes généraux, les principales différences sont la sécurité de type (cstdio n’en a pas), les performances (la plupart des implémentations d’iostreams sont plus lentes que celles de cstdio) et l’extensibilité (iostreams permet des cibles de sortie personnalisées et une sortie transparente de types définis par l’utilisateur).
L'une est une fonction qui imprime sur la sortie standard. L'autre est un objet qui fournit plusieurs fonctions membres et surcharges de operator<<
qui s'imprime sur la sortie standard. Je pourrais énumérer bien d'autres différences, mais je ne sais pas trop ce que vous cherchez.
Pour moi, les vraies différences qui me pousseraient pour "cout" plutôt que "printf" sont les suivantes:
1) << l'opérateur peut être surchargé pour mes classes.
2) Le flux de sortie pour cout peut être facilement transformé en fichier: (: copier coller :)
#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) Je trouve le cout plus lisible, surtout quand on a beaucoup de paramètres.
Un problème avec cout
correspond aux options de formatage. Le formatage des données (précision, justificaton, etc.) dans printf
est plus facile.
Deux points non mentionnés ici que je trouve importants:
1) cout
transporte beaucoup de bagages si vous n’utilisez pas déjà le STL. Il ajoute plus de deux fois plus de code à votre fichier objet que printf
. Ceci est également vrai pour string
, et c'est la principale raison pour laquelle j'ai tendance à utiliser ma propre bibliothèque de chaînes.
2) cout
utilise des opérateurs surchargés <<
, ce que je trouve malheureux. Cela peut ajouter de la confusion si vous utilisez également l'opérateur <<
pour sa fonction (décalage à gauche). Personnellement, je n'aime pas surcharger les opérateurs à des fins tangentes à leur utilisation prévue.
En bout de ligne: je vais utiliser cout
(et string
) si j'utilise déjà le fichier STL. Sinon, j'ai tendance à l'éviter.
Avec les primitives, peu importe lequel vous utilisez. Je dis que là où ça devient utile, c'est quand vous voulez sortir des objets complexes.
Par exemple, si vous avez une classe,
#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;
}
Maintenant, ce qui précède peut ne pas sembler si génial, mais supposons que vous deviez l'exporter à plusieurs endroits dans votre code. Non seulement cela, disons que vous ajoutez un champ "int d." Avec cout, il suffit de le changer une seule fois. Cependant, avec printf, il faudrait peut-être le changer dans de nombreux endroits et pas seulement, vous devez vous rappeler lesquels imprimer.
Cela dit, avec cout, vous pouvez réduire le temps passé à la maintenance de votre code et non seulement si vous réutilisez l'objet "Quelque chose" dans une nouvelle application, vous n'avez pas à vous soucier de la sortie.
J'aimerais souligner que si vous voulez jouer avec des threads en C++, si vous utilisez cout
, vous pouvez obtenir des résultats intéressants.
Considérons ce 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
Maintenant, la sortie vient tout mélangé. Cela peut aussi donner des résultats différents, essayez d’exécuter plusieurs fois:
##12:: ABABAB
##12:: ABABAB
##12:: ABABAB
##12:: ABABAB
##12:: ABABAB
Vous pouvez utiliser printf
pour bien faire les choses, ou vous pouvez utiliser mutex
.
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
S'amuser!
Bien sûr, vous pouvez écrire "quelque chose" un peu mieux pour garder la maintenance:
#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;
}
Et un test un peu plus approfondi de cout contre printf, ajouté un test de 'double', si quelqu'un veut faire plus de tests (Visual Studio 2008, version finale de l'exécutable):
#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);
}
}
Le résultat est:
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
Plus de différences: "printf" renvoie une valeur entière (égale au nombre de caractères imprimés) et "cout" ne renvoie rien
Et.
cout << "y = " << 7;
n'est pas atomique.
printf("%s = %d", "y", 7);
est atomique.
cout effectue une vérification de type, mais pas printf.
Il n'y a pas d'équivalent iostream de "% d"
J'aimerais dire que le manque d'extensibilité de printf
n'est pas tout à fait vrai:
En C, c'est vrai. Mais en C, il n'y a pas de vraies classes.
En C++, il est possible de surcharger l'opérateur de conversion, donc surcharger un opérateur char*
et utiliser printf
comme ceci:
Foo bar;
...;
printf("%s",bar);
peut être possible, si Foo surcharge le bon opérateur. Ou si vous avez fait une bonne méthode. En bref, printf
est aussi extensible que cout
pour moi.
Les arguments techniques que je peux voir pour les flux C++ (en général ... pas seulement cout.) Sont:
Typesafety. (Et, d'ailleurs, si je veux imprimer un seul '\n'
j'utilise putchar('\n')
... Je n'utiliserai pas de bombe atomique pour tuer un insecte.).
Plus simple à apprendre. (pas de paramètres "compliqués" à apprendre, juste pour utiliser les opérateurs <<
et >>
)
Travailler en mode natif avec std::string
(pour printf
, il y a std::string::c_str()
, mais pour scanf
?)
Pour printf
je vois:
Mise en forme complexe plus facile, ou du moins plus courte (en termes de caractères écrits). Bien plus lisible, pour moi (question de goût, je suppose).
Meilleur contrôle de ce que la fonction a fait (Retournez combien de caractères ont été écrits et il y a le formateur %n
: "Rien d’imprimé. L’argument doit être un pointeur sur un entier signé, où le nombre de caractères écrits jusqu’à maintenant est stocké . "(from printf - Référence C++ )
Meilleures possibilités de débogage. Pour la même raison que le dernier argument.
Mes préférences personnelles vont dans les fonctions printf
(et scanf
), principalement parce que j'aime les lignes courtes et que je ne pense pas que les problèmes de dactylographie lors de l'impression de texte sont vraiment difficiles à éviter. La seule chose que je déplore avec les fonctions de style C est que std::string
n'est pas supporté. Nous devons passer par un char*
avant de le donner à printf
(avec la std::string::c_str()
si nous voulons lire, mais comment écrire?)
Je ne suis pas un programmeur, mais j'ai été un ingénieur en facteurs humains. Je pense qu'un langage de programmation doit être facile à apprendre, à comprendre et à utiliser, ce qui nécessite une structure linguistique simple et cohérente. Bien que toutes les langues soient symboliques et donc, à la base, arbitraires, il existe des conventions et les suivre rend la langue plus facile à apprendre et à utiliser.
Il existe un grand nombre de fonctions en C++ et dans d'autres langages écrits en tant que fonction (paramètre), une syntaxe qui était à l'origine utilisée pour les relations fonctionnelles en mathématiques à l'époque pré-informatique. printf()
respecte cette syntaxe et si les rédacteurs de C++ voulaient créer une méthode logiquement différente pour lire et écrire des fichiers, ils auraient pu simplement créer une fonction différente en utilisant une syntaxe similaire.
Dans Python, nous pouvons bien sûr imprimer en utilisant la syntaxe également assez standard de object.method
, c'est-à-dire nom_variable.print, puisque les variables sont des objets, mais pas en C++.
Je ne suis pas friand de la syntaxe de cout parce que l'opérateur << ne suit aucune règle. C’est une méthode ou une fonction, c’est-à-dire qu’elle prend un paramètre et lui fait quelque chose. Cependant, il est écrit comme s'il s'agissait d'un opérateur de comparaison mathématique. C'est une mauvaise approche du point de vue des facteurs humains.
TL; DR: Faites toujours vos propres recherches concernant taille du code machine généré, performance, lisibilité et durée de codage avant de faire confiance aux commentaires aléatoires en ligne, y compris celui-ci.
Je ne suis pas un expert. Il m'est arrivé d'entendre deux collègues discuter de la façon dont nous devrions éviter d'utiliser le C++ dans les systèmes embarqués en raison de problèmes de performances. Eh bien, assez intéressant, j’ai réalisé un benchmark basé sur une tâche réelle de projet.
Dans cette tâche, nous avons dû écrire une configuration dans la RAM. Quelque chose comme:
café = chaud
sucre = néant
lait = sein
mac = AA: BB: CC: DD: EE: FF
Voici mes programmes de référence (Oui, je sais que OP a posé des questions sur printf (), pas sur fprintf (). Essayez de capturer l'essentiel et, soit dit en passant, le lien entre OP pointe de toute façon sur fprintf ().)
Programme C:
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);
Programme C++:
//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();
J'ai fait de mon mieux pour les polir avant de les boucler tous les 100 000 fois. Voici les résultats:
Programme C:
real 0m 8.01s
user 0m 2.37s
sys 0m 5.58s
Programme C++:
real 0m 6.07s
user 0m 3.18s
sys 0m 2.84s
Taille du fichier objet:
C - 2,092 bytes
C++ - 3,272 bytes
Conclusion: Sur mon très spécifique plate-forme, avec un très spécifique processeur, exécutant une version très spécifique de noyau Linux, pour exécuter un programme qui est compilé avec une version très spécifique de GCC, afin d'accomplir une très spécifique tâche, je dirais que l'approche C++ est plus appropriée car elle est bien plus rapide et fournit bien meilleure lisibilité. Par ailleurs, C offre une faible empreinte, à mon avis, ne signifie presque rien car la taille du programme ne nous concerne pas.
Rappelez-vous, YMMV.
cout<< "Hello";
printf("%s", "Hello");
Les deux sont utilisés pour imprimer des valeurs. Ils ont une syntaxe complètement différente. C++ a les deux, C n'a que printf.