'printf' vs 'cout' en C ++

Réponses:

333

Je suis surpris que tout le monde dans cette question affirme que std::coutc'est bien mieux que printf, même si la question demandait simplement des différences. Maintenant, il y a une différence - std::coutc'est C ++, et printfc'est C (cependant, vous pouvez l'utiliser en C ++, comme presque n'importe quoi d'autre de C). Maintenant, je vais être honnête ici; les deux printfet std::coutont leurs avantages.

De vraies différences

Extensibilité

std::coutest extensible. Je sais que les gens diront que printfc'est également extensible, mais une telle 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 commune), et de telles extensions ne sont qu'une lettre (il est donc facile d'entrer en conflit avec un format déjà existant).

Contrairement à printf, std::coutdépend entièrement de la surcharge de l'opérateur, il n'y a donc aucun problème avec les formats personnalisés - tout ce que vous faites est de définir un sous-programme en prenant std::ostreamle premier argument et votre type en second. En tant que tel, il n'y a pas de problèmes d'espace de noms - tant que vous avez une classe (qui n'est pas limitée à un caractère), vous pouvez avoir du travailstd::ostream surcharge de pour elle.

Cependant, je doute que beaucoup de gens voudraient étendre ostream(pour être honnête, j'ai rarement vu de telles extensions, même si elles sont faciles à faire). Cependant, il est là si vous en avez besoin.

Syntaxe

Comme il pourrait être facilement remarqué, à la fois printfet std::coututiliser une syntaxe différente. printfutilise la syntaxe de fonction standard en utilisant une chaîne de modèle et des listes d'arguments de longueur variable. En fait, printfc'est une raison pour laquelle C en a - les printfformats sont trop complexes pour être utilisables sans eux. Cependant, std::coututilise une API différente - l' operator <<API qui se retourne.

Généralement, 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 le numéro d'erreur et que sa description est un espace réservé, le code ressemblerait à ceci. Les deux exemples fonctionnent de manière identique (enfin, en quelque sorte, std::endlvide 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 des arguments, au lieu de simplement les imprimer. Par exemple, l'impression de quelque chose comme ça 0x0424est tout simplement fou. Cela est dû au std::coutmélange de l'état et des valeurs réelles. Je n'ai jamais vu un langage où quelque chose comme std::setfillserait un type (autre que C ++, bien sûr). printfsépare clairement les arguments et le type réel. Je préférerais vraiment en conserver la printfversion (même si elle a l'air un peu cryptique) par rapport à sa iostreamversion (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 réside le véritable avantage du printfmensonge. La printfchaîne de format est bien ... une chaîne. Cela rend la traduction vraiment facile par rapport à un operator <<abus de iostream. En supposant que la gettext()fonction traduit et que vous souhaitez afficher Error 2: File not found., le code pour 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 fausses iostreamconstructions printfauxquelles vous pouvez passer gettext, ou quelque chose, à des fins de traduction. Bien sûr, ce $n'est pas la norme C, mais c'est tellement courant qu'il est sûr de l'utiliser à mon avis.

Ne pas avoir à mémoriser / rechercher la syntaxe de type entier spécifique

C a beaucoup de types entiers, tout comme C ++. std::coutgère tous les types pour vous, tout en printfnécessitant une syntaxe spécifique en fonction d'un type entier (il existe des types non entiers, mais le seul type non entier que vous utiliserez en pratique avec printfest const char *(la chaîne C, peut être obtenue en utilisant la to_cméthode de std::string)). Par exemple, pour imprimer size_t, vous devez utiliser %zd, tandis que vous int64_tdevrez utiliser %"PRId64". Les tableaux sont disponibles sur http://en.cppreference.com/w/cpp/io/c/fprintf et http://en.cppreference.com/w/cpp/types/integer .

Vous ne pouvez pas imprimer l'octet NUL, \0

Parce qu'il printfutilise des chaînes C par opposition aux chaînes C ++, il ne peut pas imprimer d'octet NUL sans astuces spécifiques. Dans certains cas, il est possible d'utiliser %cavec '\0'comme argument, bien que ce soit clairement un hack.

Différences dont personne ne se soucie

Performance

Mise à jour: Il s'avère que iostreamc'est si lent qu'il est généralement plus lent que votre disque dur (si vous redirigez votre programme vers un fichier). La désactivation de la synchronisation avec stdiopeut être utile si vous avez besoin de générer de nombreuses données. Si les performances sont un réel problème (par opposition à l'écriture de plusieurs lignes dans STDOUT), utilisez simplementprintf .

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 de toute façon un goulot d'étranglement, peu importe si vous utilisez printfou iostream. Je pense que cela printf pourrait être plus rapide d'un rapide coup d'œil à l'assemblage (compilé avec clang en utilisant l' -O3option du compilateur). En supposant mon exemple d'erreur, l' printfexemple fait beaucoup moins d'appels que l' coutexemple. C'est int mainavec 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ées comme printfarguments. C'est à peu près ça; il n'y a rien d'autre. À titre de comparaison, cela est iostreamcompilé pour l'assemblage. Non, il n'y a pas de doublure; chaque operator <<appel unique 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 ce iostreamn'est pas plus rapide parce que c'est "type safe". La plupart des implémentations C implémentent des printfformats utilisant goto calculé, donc printfc'est aussi rapide que possible, même sans que le compilateur en soit conscient printf(pas qu'ils ne le soient pas - certains compilateurs peuvent optimiser printfdans certains cas - la chaîne constante se terminant par \nest généralement optimisée pourputs ) .

Héritage

Je ne sais pas pourquoi tu voudrais hériter ostream, mais je m'en fiche. C'est possible avec FILEaussi.

class MyFile : public FILE {}

Type de sécurité

Certes, les listes d'arguments de longueur variable n'ont aucune sécurité, mais cela n'a pas d'importance, car les compilateurs C populaires peuvent détecter des problèmes avec la printfchaî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);
     ^
Konrad Borowski
la source
18
Vous dites que les E / S sont de toute façon le goulot d'étranglement. De toute évidence, vous n'avez jamais testé cette hypothèse. Je cite moi-même: "D'un autre côté, la version iostreams, à 75,3 Mo / s, ne peut pas mettre en mémoire tampon les données assez rapidement pour suivre un disque dur. C'est mauvais, et cela ne fait même pas encore de vrai travail. Je ne Je ne pense pas que mes attentes soient trop élevées lorsque je dis que ma bibliothèque d'E / S devrait pouvoir saturer mon contrôleur de disque. "
Ben Voigt
4
@BenVoigt: J'avoue, j'essaie d'éviter C ++ lorsque c'est possible. J'ai beaucoup essayé de l'utiliser, mais c'était plus ennuyeux et moins maintenable que les autres langages de programmation que j'utilisais. C'est encore une autre raison pour moi d'éviter C ++ - ce n'est même pas rapide (ce n'est même pas iostream - la bibliothèque C ++ entière est lente dans la plupart des implémentations, peut-être à l'exception de std::sort, qui est en quelque sorte étonnamment rapide par rapport à qsort(2 fois), à coût de la taille exécutable).
Konrad Borowski
3
Personne ici n'a mentionné de problèmes dans un environnement parallèle lors de l'utilisation de cout.
Nicholas Hamilton
9
Votre argument de performance n'a aucun sens. Plus d'assemblage dans votre programme ne signifie pas que ce programme sera plus lent, car vous ne tenez pas compte de tout le code qui fait la fonction printf, ce qui est beaucoup de code. À mon avis, il est possible d'optimiser le cout avec l'opérateur << beaucoup mieux que le printf, car le compilateur peut mieux comprendre les variables et le formatage.
Ignas2526
18
J'aime beaucoup de choses sur cette réponse, mais peut-être que ma partie préférée est "Tout le monde pense qu'ils se soucient de la performance, mais personne ne se soucie de la mesurer."
Kyle Strand
203

De la FAQ C ++ :

[15.1] Pourquoi devrais-je utiliser à la <iostream> place du traditionnel <cstdio>?

Augmentez la sécurité des types, réduisez les erreurs, permettez l'extensibilité et fournissez l'héritabilité.

printf()n'est sans doute pas cassé, et scanf()est peut-être habitable malgré le risque d'erreur, mais les deux sont limités en ce qui concerne ce que les E / S C ++ peuvent faire. Les E / S C ++ (en utilisant <<et >>) sont, par rapport à C (en utilisant printf()et scanf()):

  • Plus sûr pour <iostream>le type : Avec , le type d'objet étant I / O'd est connu statiquement par le compilateur. En revanche, <cstdio>utilise les 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 qui sont I / O'd. La suppression de la redondance supprime une classe d'erreurs.
  • Extensible: Le <iostream>mécanisme C ++ 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()et scanf()?!
  • Héritable: Le <iostream>mécanisme C ++ est construit à partir de classes réelles telles que std::ostreamet std::istream. Contrairement à <cstdio>« s FILE*, ce sont des classes réelles et , par conséquent héritable. 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 millions 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 de "flux étendu".

D'autre part, printfest beaucoup plus rapide, ce qui peut justifier l' utilisation de préférence à couten très cas spécifiques et limitées. Profilez toujours en premier. (Voir, par exemple, http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /)

Marcelo Cantos
la source
2
D'un autre côté, il y a la bibliothèque FastFormat ( fastformat.org ), qui offre à la fois sécurité de type, expressivité et performance. (Pas que je l'ai encore essayé ...)
xtofl
3
@Marcelo probablement parce que c'est un bon résumé, avec tout ce qui est cité. Le formatage ... oui, c'est assez mauvais. J'aurais dû résoudre ce problème moi-même, mais il semble que d'autres (vous y compris) s'en soient occupés, ce qui, bien sûr, est plus constructif que de se plaindre.
Mikeage
2
Ces derniers temps, il printf()est également censé être extensible. Voir "printf hooks" sur udrepper.livejournal.com/20948.html
Maxim Egorushkin
4
@MaximYegorushkin: Standard printfn'a pas une telle capacité. Les mécanismes de bibliothèque non portables ne sont guère au même niveau que l'extensibilité entièrement standardisée des flux ios.
Ben Voigt
4
"D'un autre côté, printf est beaucoup plus rapide" printf est également plus propre et plus facile à utiliser, c'est pourquoi j'évite les couts quand c'est possible.
FluorescentGreen5
43

Les gens prétendent souvent que printfc'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 des retours à la ligne, utilisez printf; sinon, coutest 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 iostreams valent toujours mieux que printf; J'essaie simplement de dire que vous devriez prendre une décision éclairée basée sur des données réelles, et non une supposition sauvage basée sur une hypothèse commune et trompeuse.

Mise à jour: voici le code complet que j'ai utilisé pour les tests. Compilé g++sans aucune option supplémentaire (sauf -lrtpour le 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);
    }
}
Thomas
la source
5
Dans vos scores, printf bat facilement cout (cas majoritaires). Je me demande pourquoi vous recommandez d'utiliser cout en matière de perf. Bien que je convienne que la perf n'est pas trop différente dans les cas réalistes ..
mishal153
3
@ mishal153: J'essaie juste de dire que les performances ne sont pas trop différentes, donc le conseil communément entendu de "ne jamais utiliser cout parce que c'est lent" est tout simplement stupide. Notez que cout a l'avantage évident de la sécurité des caractères, et souvent aussi de la lisibilité. (Le formatage en virgule flottante avec iostreams est horrible ...)
Thomas
35
La différence importante entre printf()et std::ostreamest que le premier produit tous les arguments en un seul appel alors qu'il std::ostreamengendre un appel distinct pour chacun <<. Le test ne produit qu'un seul argument et une nouvelle ligne, c'est pourquoi vous ne pouvez pas voir la différence.
Maxim Egorushkin,
12
Le compilateur devrait être en mesure d'inline ces appels. En outre, il printfpourrait faire beaucoup d'appels sous les couvertures pour des fonctions d'assistance pour divers spécificateurs de formatage ... cela, ou c'est une fonction monolithique monstrueuse. Et encore une fois, en raison de la doublure, cela ne devrait pas du tout faire une différence de vitesse.
Thomas
4
Vous avez chronométré votre terminal. Utilisez sprintfou fprintfet stringstreamou fstream.
Ben Voigt
41

Et je cite :

En termes de haut niveau, les principales différences sont la sécurité des types (cstdio ne l'a pas), les performances (la plupart des implémentations iostreams sont plus lentes que celles cstdio) et l'extensibilité (iostreams permet des cibles de sortie personnalisées et une sortie transparente des types définis par l'utilisateur).

Kyle Rosendo
la source
Surtout sur unix où avec POSIX vous ne savez jamais quelle taille a réellement un typedefs, vous avez donc besoin de beaucoup de transtypages ou comme 99% des programmes vous le risquez simplement avec% d. Il a fallu encore longtemps avant que% z ne soit livré avec C99. Mais pour time_t / off_t, la quête de la bonne instruction de format se poursuit.
Lothar
30

L'une est une fonction qui imprime sur stdout. L'autre est un objet qui fournit plusieurs fonctions membres et surcharges de operator<<cette impression à stdout. Il y a beaucoup plus de différences que je pourrais énumérer, mais je ne suis pas sûr de ce que vous recherchez.

Marcelo Cantos
la source
12

Pour moi, les vraies différences qui me feraient opter pour 'cout' plutôt que pour 'printf' sont:

1) << L' opérateur peut être surchargé pour mes classes.

2) Le flux de sortie pour cout peut être facilement changé en un 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 cout plus lisible, surtout quand on a beaucoup de paramètres.

Un problème avec coutles options de formatage. Le formatage des données (précision, justification, etc.) printfest plus simple.

mishal153
la source
1
c'est bien. Comment puis-je savoir que personne ne modifie le cout global de cette façon dans un thread de bibliothèque étranger?
vp_arth
1
Vous pouvez facilement passer printfà un fichier en le remplaçant par fprintf...
CoffeeTableEspresso
5

Deux points non mentionnés ici que je trouve importants:

1) couttransporte beaucoup de bagages si vous n'utilisez pas déjà la STL. Il ajoute plus de deux fois plus de code à votre fichier objet que printf. Cela 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) coututilise des <<opérateurs surchargés , ce que je trouve regrettable. Cela peut ajouter de la confusion si vous utilisez également le<< opérateur aux fins prévues (décalage vers la gauche). Personnellement, je n'aime pas surcharger les opérateurs à des fins tangentielles à leur utilisation prévue.

Conclusion: je vais utiliser cout(et string) si j'utilise déjà la STL. Sinon, j'ai tendance à l'éviter.

Bill Weinman
la source
4

Avec les primitives, peu importe celle que vous utilisez. Je dis où cela 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 génial, mais supposons que vous deviez le produire à plusieurs endroits dans votre code. Non seulement cela, disons que vous ajoutez un champ "int d". Avec cout, vous n'avez qu'à le changer en une seule fois. Cependant, avec printf, vous devrez le changer dans peut-être beaucoup d'endroits et non seulement cela, vous devez vous rappeler ceux à sortir.

Cela dit, avec cout, vous pouvez réduire beaucoup de temps passé à la maintenance de votre code et pas seulement si vous réutilisez l'objet "Quelque chose" dans une nouvelle application, vous n'avez pas vraiment à vous soucier de la sortie.

Daniel
la source
En outre, pour ajouter des informations sur les performances, je dirais que vous ne devriez rien produire du tout si votre application est conçue pour les performances. Tout type de sortie vers std est assez cher et lent. Je dis que vous devriez l'éviter et ne produire que lorsque cela est absolument nécessaire.
Daniel
gardez à l'esprit que votre classe peut avoir des membres privés auxquels vous ne pouvez pas accéder aussi facilement de l'extérieur. Avec l'opérateur de sortie, vous avez exactement un emplacement qui doit être ami avec votre classe, et maintenant vous pouvez le sortir n'importe où, même dans du code que vous ne connaissiez pas.
hochl
2

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 étendu de cout vs printf, a 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
LuP
la source
Wow, pourquoi est- endlce tellement moins efficace que '\n'?
Nicholas Hamilton
1
Je crois que c'est parce qu'il endlvide le tampon, et \nnon, bien que je ne sois pas sûr que ce soit définitivement la raison.
Caleb Xu
Ce n'est pas une réponse à la question, c'est plutôt une réponse à celle de Daniel et Thomas .
Fabio dit Réintégrer Monica le
2

Je voudrais souligner que si vous voulez jouer avec des threads en C ++, si vous utilisez, coutvous pouvez obtenir des résultats intéressants.

Considérez 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 est mélangée. Il peut également donner des résultats différents, essayez de l'exécuter plusieurs fois:

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

Vous pouvez utiliser printfpour 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!

Apollon
la source
2
les wtf threadne rendent pas la sortie folle. Je viens de reproduire et de trouver à la fois xyzet ABCdans la sortie. Il n'y avait pas de bangleries b / w ABCcomme ABABAB.
Abhinav Gauniyal
1
Je ne sais pas comment coutfonctionne les threads, mais je suis sûr que le code que vous affichez n'est pas celui que vous avez utilisé pour obtenir ces sorties. Votre code passe la chaîne "ABC"pour le thread 1 et "xyz"pour le thread 2, mais votre sortie affiche AAAet BBB. Veuillez le corriger, car en ce moment, c'est déroutant.
Fabio dit Réintégrer Monica le
1
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.

scatman
la source
19
... quoi? avez-vous mélangé quelque chose?
xtofl
1
Correction du problème. -1 car cela a nécessité une correction et la réponse laisse beaucoup à désirer.
Yacoby
3
Les noms des fonctions ont été inversés: cout a été utilisé avec la syntaxe printf et printf a été utilisé avec la syntaxe cout. Ça n'aurait même pas dû être accepté!
Mahmoud Al-Qudsi
2
et le principal inconvénient de cout est qu'il utilise l'opérateur << qui est verbeux et laid et peut-être abusif de l'opérateur. :)
2010
8
Bien que ce ne soit certainement pas la meilleure réponse, je ne comprends pas comment Scatman est puni pour sa réponse uniquement parce qu'elle a été choisie comme la meilleure réponse. xbit a une réponse bien pire à l'OMI mais a -1 vote. Je ne dis pas que xbit devrait être rejeté, mais je ne pense pas qu'il soit juste de voter contre scatman pour l'erreur du PO plus qu'il ne doit l'être ...
Jesse
1

Je voudrais dire que le manque d'extensibilité de printfn'est pas entièrement 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 cast, donc, surcharger un char*opérateur et utiliser printfcomme 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. Bref, printfc'est aussi extensible que coutpour moi.

L'argument technique que je peux voir pour les flux C ++ (en général ... pas seulement cout.) Sont:

  • Sécurité de la typographie. (Et, au fait, si je veux imprimer un seul '\n'que 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 à utiliser <<et >>opérateurs)

  • Travaillez nativement avec std::string(car printfil y en a std::string::c_str(), mais pour scanf?)

Car printfje vois:

  • Mise en forme complexe plus facile ou au moins plus courte (en termes de caractères écrits). Beaucoup plus lisible, pour moi (question de goût je suppose).

  • Meilleur contrôle de ce que la fonction a fait (Renvoyez le nombre de caractères écrits et le %nformateur: "Rien imprimé. L'argument doit être un pointeur vers un entier signé, où le nombre de caractères écrits jusqu'ici est stocké." ( Tiré de 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 aux fonctions printf(et scanf), principalement parce que j'aime les lignes courtes et parce que je ne pense pas que les problèmes de frappe sur l'impression de texte soient vraiment difficiles à éviter. La seule chose que je déplore avec les fonctions de style C, c'est que ce std::stringn'est pas supporté. Nous devons passer par un char*avant de le donner printf(avec le std::string::c_str()si nous voulons lire, mais comment écrire?)

bmorel
la source
3
Le compilateur n'a pas d'informations de type pour les fonctions varargs, il ne convertira donc pas le paramètre réel (à l'exception des promotions d'arguments par défaut , telles que les promotions intégrales standard). Voir 5.2.2p7. Une conversion définie par l'utilisateur char*ne sera pas utilisée.
Ben Voigt
Même si cela fonctionnait, ce ne serait pas un exemple d'extensibilité de sprintf, juste un hack intelligent pour donner à sprintf ce qu'il attend, et il ignore certains problèmes graves tels que où char*vivent et pour combien de temps, et les dangers de la définition par l'utilisateur lancements implicites.
Marcelo Cantos
1

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 la vérification typographique, printf non.

Il n'y a pas d'équivalent iostream de "% d"

skan
la source
3
coutne renvoie rien car c'est un objet, pas une fonction. operator<<renvoie quelque chose (normalement son opérande gauche, mais une valeur fausse s'il y a une erreur). Et dans quel sens est l' printfappel "atomique"?
Keith Thompson
9
C'est comme une bombe atomique. printf("%s\n",7);
bruit sans art
@artlessnoise attendre pourquoi faute de segmentation? %sest ?
Abhinav Gauniyal
1
C'est le point de la déclaration de la «bombe atomique». Un argument printf % s doit avoir un pointeur valide vers une chaîne terminée par null. La plage de mémoire «7» (un pointeur) n'est généralement pas valide; un défaut de segmentation pourrait être chanceux. Sur certains systèmes, «7» peut imprimer beaucoup de déchets sur une console et vous devrez le regarder pendant une journée avant l'arrêt du programme. En d'autres termes, c'est une mauvaise chose printf. Les outils d'analyse statique peuvent détecter bon nombre de ces problèmes.
bruit sans art
Bien que techniquement printfne fasse pas de vérification typographique, je n'ai jamais utilisé de compilateur qui ne m'avertisse pas d'erreurs de type avec printf...
CoffeeTableEspresso
1

TL; DR: faites toujours vos propres recherches en ce qui concerne la taille du code machine généré , les performances , lisibilité et le temps de codage avant de faire confiance à des commentaires aléatoires en ligne, y compris celui-ci.

Je ne suis pas un expert. Je viens d'entendre deux collègues parler de la façon dont nous devrions éviter d'utiliser C ++ dans les systèmes embarqués en raison de problèmes de performances. Eh bien, assez intéressant, j'ai fait un benchmark basé sur une vraie tâche de projet.

Dans cette tâche, nous avons dû écrire une configuration sur la RAM. Quelque chose comme:

café =
sucre chaud = aucun
lait = macarine de poitrine
= 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'essence et, en passant, le lien d'OP pointe vers fprintf () de toute façon.)

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 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 ma plateforme très spécifique , avec un processeur très spécifique , exécutant une version très spécifique du noyau Linux , pour exécuter un programme qui est compilé avec une version très spécifique de GCC , afin d'accomplir une tâche très spécifique , je dirais l'approche C ++ est plus appropriée car elle s'exécute beaucoup plus rapidement et offre une bien meilleure lisibilité. D'un autre côté, C offre une petite empreinte, à mon avis, ne signifie presque rien parce que la taille du programme n'est pas notre préoccupation.

Remeber, YMMV.

Wesley
la source
Je ne suis pas d'accord que C ++ est plus lisible dans cet exemple, car votre exemple regroupe plusieurs lignes dans un seul appel printf. C'est naturellement moins lisible que la façon dont vous avez fait le code C ++, et cela se fait rarement en C car c'est difficile à lire et difficile à maintenir. Une comparaison équitable répartirait le C en printfs séparés, un pour la ligne de portée.
maharvey67
1
@ maharvey67 C'est vrai ce que tu as dit. Cependant, l'exemple que j'ai donné dans C était en considération de la performance. L'appel emballé en un à fprintf était déjà deux secondes plus lent que l'équivalence C ++. Si je devais rendre le code C lisible, cela pourrait être encore plus lent. Avertissement: c'était il y a un an et je me souviens avoir fait de mon mieux pour peaufiner le code C et C ++. Je n'avais aucune preuve d'appels séparés à fprintf serait plus rapide qu'un seul appel, mais la raison pour laquelle je l'ai fait de cette manière indique probablement que ce n'était pas le cas.
Wesley
0

Je ne suis pas programmeur, mais j'ai été ingénieur en facteurs humains. Je pense qu'un langage de programmation devrait être facile à apprendre, à comprendre et à utiliser, ce qui nécessite qu'il ait 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 facilite l'apprentissage et l'utilisation de la langue.

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'ère pré-informatique. printf()suit cette syntaxe et si les auteurs 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.

En Python, nous pouvons bien sûr imprimer en utilisant également object.method syntaxe , c'est-à-dire variablename.print, car les variables sont des objets, mais en C ++, elles ne le sont pas.

Je n'aime pas la syntaxe cout car 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 y fait quelque chose. Cependant, il est écrit comme s'il s'agissait d'un opérateur de comparaison mathématique. Il s'agit d'une mauvaise approche du point de vue des facteurs humains.

Daniel Woodard
la source
-1

printfest une fonction alors que coutest une variable.

John
la source
6
J'ai fait un retour en arrière parce que, même si la réponse elle-même peut être fausse, c'est toujours une vraie réponse. Si vous pensez (correctement) que la réponse est fausse, vous avez deux options: 1) ajouter un commentaire ou 2) ajouter une nouvelle réponse (ou faire les deux). Ne changez pas la réponse de quelqu'un pour qu'elle dise quelque chose de complètement différent de ce qui était prévu par l'auteur.
Mark
1
printfest une fonction, mais printf()est un appel de fonction =)
vp_arth
cout est un objet, pas une variable.
Lin