web-dev-qa-db-fra.com

'printf' contre 'cout' en C ++

Quelle est la différence entre printf() et cout en C++?

325
hero

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.

Différences réelles

Extensibilité

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.

Syntaxe

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;

Traduction

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.

Ne pas avoir à se rappeler/rechercher la syntaxe de type entier spécifique

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 .

Vous ne pouvez pas imprimer l'octet NUL, \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.

Des différences dont personne ne se soucie

Performance

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).

Héritage

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 {}

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 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);
     ^
275
Konrad Borowski

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é, et scanf() 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 utilisant printf() et scanf()):

  • 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() et scanf() ?!
  • Inheritable: le mécanisme C++ <iostream> est construit à partir de classes réelles telles que std::ostream et std::istream. Contrairement à <cstdio>'s FILE*, 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 /)

197
Mikeage

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 iostreams 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);
    }
}
40
Thomas

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).

38
Kyle Rozendo

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.

30
Marcelo Cantos

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.

12
mishal153

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.

5
Bill Weinman

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.

4
Daniel

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!

2
Apollo

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
2
LuP

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"

1
skan

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?)

1
bmorel

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.

1
Daniel Woodard

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.

1
Wesley
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.

0
scatman