web-dev-qa-db-fra.com

Devrais-je utiliser printf dans mon code C++?

J'utilise généralement cout et cerr pour écrire du texte sur la console. Cependant, je trouve parfois plus facile d'utiliser la bonne vieille instruction printf. Je l'utilise quand j'ai besoin de formater la sortie. 

Voici un exemple d'utilisation:

// Lets assume that I'm printing coordinates... 
printf("(%d,%d)\n", x, y);

// To do the same thing as above using cout....
cout << "(" << x << "," << y << ")" << endl;

Je sais que je peux formater la sortie en utilisant cout mais je sais déjà comment utiliser printf. Y a-t-il une raison pour laquelle je ne devrais pas utiliser l'instruction printf?

67
Bob Dylan

Mes étudiants, qui apprennent d'abord cin et cout, puis apprennent printf plus tard, préfèrent pour la majorité printf (ou plus généralement fprintf). J'ai moi-même trouvé le modèle printf suffisamment lisible pour le porter dans d'autres langages de programmation. Il en va de même de (Olivier Danvy } _, qui l'a même rendu sûr.

Si vous avez un compilateur capable de vérifier le type des appels à printf, je ne vois aucune raison de ne pas utiliser fprintf et d'amis en C++.

Disclaimer: Je suis un mauvais programmeur C++.

68
Norman Ramsey

Si vous espérez intégrer votre programme, éloignez-vous de iostreams. Le problème est qu'il peut être impossible de localiser correctement vos chaînes si la phrase est composée de plusieurs fragments, comme c'est le cas avec iostream.

Outre la question des fragments de message, vous avez également un problème de commande. Considérez un rapport indiquant le nom d’un élève et sa moyenne cumulative:

std::cout << name << " has a GPA of " << gpa << std::endl;

Lorsque vous traduisez cela dans une autre langue, la grammaire de cette langue peut vous demander de montrer le GPA avant le nom. Autant que je sache, iostreams n’a aucun moyen de réorganiser les valeurs interpolées.

Si vous voulez le meilleur des deux mondes (type safety et capacité de i18n), utilisez Boost.Format .

48
R Samuel Klatchko

Adaptabilité

Toute tentative de printf d'un non-POD entraîne un comportement indéfini:

struct Foo { 
    virtual ~Foo() {}
    operator float() const { return 0.f; }
};

printf ("%f", Foo());

std::string foo;
printf ("%s", foo);

Les appels printf ci-dessus donnent un comportement indéfini. Votre compilateur peut vous avertir, mais ces avertissements ne sont pas requis par les normes et ne sont pas possibles pour les chaînes de format connues uniquement au moment de l'exécution.

IO-Streams:

std::cout << Foo();
std::string foo;
std::cout << foo;

Jugez vous-même.

Extensibilité

struct Person {
    string first_name;
    string second_name;
};
std::ostream& operator<< (std::ostream &os, Person const& p) {
    return os << p.first_name << ", " << p.second_name;
}

cout << p;
cout << p;
some_file << p;

C:

// inline everywhere
printf ("%s, %s", p.first_name, p.second_name);
printf ("%s, %s", p.first_name, p.second_name);
fprintf (some_file, "%s, %s", p.first_name, p.second_name);

ou:

// re-usable (not common in my experience)
int person_fprint(FILE *f, const Person *p) {
    return fprintf(f, "%s, %s", p->first_name, p->second_name);
}
int person_print(const Person *p) {
    return person_fprint(stdout, p);
}

Person p;
....
person_print(&p);

Notez que vous devez prendre soin d'utiliser les arguments/signatures d'appel appropriés en C (par exemple, person_fprint(stderr, ..., person_fprint(myfile, ...), où en C++, "l'argument FILE" est automatiquement "dérivé" de l'expression. Un équivalent plus exact de cette dérivation ressemble en réalité davantage à ceci:

FILE *fout = stdout;
...
fprintf(fout, "Hello World!\n");
person_fprint(fout, ...);
fprintf(fout, "\n");

I18N

Nous réutilisons notre définition de personne:

cout << boost::format("Hello %1%") % p;
cout << boost::format("Na %1%, sei gegrüßt!") % p;

printf ("Hello %1$s, %2$s", p.first_name.c_str(), p.second_name.c_str()); 
printf ("Na %1$s, %2$s, sei gegrüßt!", 
        p.first_name.c_str(), p.second_name.c_str()); 

Jugez-vous vous-même. </ Strike>

Je trouve cela moins pertinent à compter d'aujourd'hui (2017). Peut-être juste un sentiment instinctif, mais I18N n’est pas quelque chose qui est fait quotidiennement par votre programmeur moyen C ou C++. De plus, c'est une douleur dans la ... natomie de toute façon.

Performance

  1. Avez-vous mesuré l’importance réelle des performances de printf? Vos applications de goulot d'étranglement sont-elles si sérieusement paresseuses que le résultat des calculs est un goulot d'étranglement? Êtes-vous sûr d'avoir besoin de C++?
  2. La pénalité de performances redoutée est de satisfaire ceux d’entre vous qui souhaitent utiliser une combinaison de printf et de cout. C'est une fonctionnalité, pas un bug!

Si vous utilisez régulièrement iostreams, vous pouvez 

std::ios::sync_with_stdio(false);

et récolter un temps d'exécution égal avec un bon compilateur:

#include <cstdio>
#include <iostream>
#include <ctime>
#include <fstream>

void ios_test (int n) {
    for (int i=0; i<n; ++i) {
        std::cout << "foobarfrob" << i;
    }
}

void c_test (int n) {
    for (int i=0; i<n; ++i) {
        printf ("foobarfrob%d", i);
    }
}


int main () {
    const clock_t a_start = clock();
    ios_test (10024*1024);
    const double a = (clock() - a_start) / double(CLOCKS_PER_SEC);

    const clock_t p_start = clock();
    c_test (10024*1024);
    const double p = (clock() - p_start) / double(CLOCKS_PER_SEC);

    std::ios::sync_with_stdio(false);
    const clock_t b_start = clock();
    ios_test (10024*1024);
    const double b = (clock() - b_start) / double(CLOCKS_PER_SEC);


    std::ofstream res ("RESULTS");
    res << "C ..............: " << p << " sec\n"
        << "C++, sync with C: " << a << " sec\n"
        << "C++, non-sync ..: " << b << " sec\n";
}

Résultats (g++ -O3 synced-unsynced-printf.cc, ./a.out > /dev/null, cat RESULTS):

C ..............: 1.1 sec
C++, sync with C: 1.76 sec
C++, non-sync ..: 1.01 sec

Juge ... toi-même.

Non, vous ne m'interdirez pas mon printf.

Grâce à des modèles variés, vous pouvez imprimer en C++ 11 avec la sécurité typographique I18N. Et vous pourrez les avoir très, très performants en utilisant des littéraux définis par l’utilisateur, c’est-à-dire qu’il sera possible d’écrire une incarnation totalement statique.

J'ai une preuve de concept . À l'époque, la prise en charge de C++ 11 n'était pas aussi avancée que maintenant, mais vous avez une idée.

Adaptabilité temporelle

// foo.h
...
struct Frob {
    unsigned int x;
};
...

// alpha.cpp
... printf ("%u", frob.x); ...

// bravo.cpp
... printf ("%u", frob.x); ...

// charlie.cpp
... printf ("%u", frob.x); ...

// delta.cpp
... printf ("%u", frob.x); ...

Plus tard, vos données deviennent si volumineuses que vous devez le faire 

// foo.h
...
    unsigned long long x;
...

C’est un exercice intéressant qui maintient cela et le fait sans aucun bug. Surtout quand d'autres projets non couplés utilisent foo.h.

Autre.

  • Bug Potential: Il y a beaucoup d'espace pour commettre des erreurs avec printf, en particulier lorsque vous ajoutez des chaînes de bases d'entrées utilisateur (pensez à votre équipe I18N). Vous devez prendre soin de bien échapper à chacune de ces chaînes de format, vous devez être sûr de passer les bons arguments, etc. etc.

  • IO-Streams agrandit mon fichier binaire: s'il s'agit d'un problème plus important que la maintenabilité, la qualité du code, la réutilisabilité, utilisez (après vérification du problème!), Printf.

21
Sebastian Mach

Utilisez boost :: format. Vous obtenez le type de sécurité, le support std :: string, une interface semblable à printf, la possibilité d'utiliser cout et beaucoup d'autres choses intéressantes. Vous ne reviendrez pas.

19
janm

J'utilise printf parce que je déteste la syntaxe laide <<cout<<

19
Igor Zevaka

Aucune raison du tout. Je pense que c'est juste une étrange idéologie qui pousse les gens à utiliser uniquement les bibliothèques C++, même si les bonnes vieilles bibliothèques C sont toujours valables. Je suis un gars C++ et j'utilise beaucoup les fonctions C aussi. Je n'ai jamais eu de problèmes avec eux.

7
mingos

Les flux sont la voie canonique. Essayez de faire fonctionner ce code avec printf:

template <typename T>
void output(const T& pX)
{
    std::cout << pX << std::endl;
}

Bonne chance.

Ce que je veux dire, c’est que vous pouvez faire en sorte que les opérateurs autorisent la sortie de vos types dans ostream, et l’utilisez sans problème comme tout autre type. printf ne correspond pas à la généralité de C++, ni plus spécifiquement aux modèles.

Il y a plus que la convivialité. Il y a aussi la cohérence. Dans tous mes projets, j'ai couté (et cerr et clog) de sortir également dans un fichier. Si vous utilisez printf, vous ignorez tout cela. De plus, la cohérence elle-même est une bonne chose. mélanger cout et printf, bien que parfaitement valide, est laide.

Si vous avez un objet et que vous voulez le rendre utilisable en sortie, la méthode la plus propre consiste à surcharger operator<< pour cette classe. Comment allez-vous utiliser printf alors? Vous allez vous retrouver avec du code mélangé avec des variables cout et printf.

Si vous voulez vraiment formater, utilisez Boost.Format tout en maintenant l'interface de flux. Cohérence et formatage.

6
GManNickG

Utilisez printf. N'utilisez pas de flux C++. printf vous donne un bien meilleur contrôle (comme la précision du flottant, etc.). Le code est également généralement plus court et plus lisible.

Guide de style de Google C++ accepte. 

Ne pas utiliser de cours d'eau, sauf où requis par une interface de journalisation. Utilisation des routines similaires à printf.

Il y a divers avantages et inconvénients à en utilisant des flux, mais dans ce cas, comme dans de nombreux autres cas, la cohérence des atouts le débat. N'utilisez pas de flux dans votre code.

5
cuteCAT

Dans l'ensemble, je suis d'accord (déteste la syntaxe << surtout si vous avez besoin d'une mise en forme complexe) 

Mais je devrais souligner les aspects de sécurité.

printf("%x",2.0f)
printf("%x %x",2)
printf("%x",2,2)

Ne sera probablement pas remarqué par le compilateur mais pourrait planter votre application.

5
Martin Beckett

Utilisez ce qui correspond à vos besoins et préférences. Si vous êtes à l'aise avec printf, utilisez-le. Si vous êtes plus heureux avec iostreams, respectez-le. Mélangez et assortissez le mieux à vos besoins. Après tout, il s’agit d’un logiciel: il existe de meilleures méthodes et des méthodes pires, mais il n’existe que rarement UN SEUL moyen.

Partager et profiter.

4
Bob Jarvis

Je n'aime pas printf. Son manque de sécurité de type rend son utilisation dangereuse, et la nécessité de se rappeler les spécificateurs de format est une douleur. Les opérateurs basés sur un modèle qui font intelligemment la bonne chose sont bien meilleurs. Donc, j'utilise toujours les flux C++ en C++.

Certes, beaucoup de gens préfèrent printf, pour autres raisons, énumérées ailleurs.

3
Paul Nathan

Je «retourne souvent» à l’utilisation de printf(), mais le plus souvent snprintf() pour une sortie formatée plus facile. Lors de la programmation en C++, j'utilise ce wrapper que j'ai écrit il y a quelque temps, appelé ainsi (pour utiliser votre exemple comme ci-dessus): cout << format("(%d,%d)\n", x, y);

Voici l'en-tête (stdiomm.h):

#pragma once

#include <cstdarg>
#include <string>

template <typename T>
std::basic_string<T> format(T const *format, ...);

template <typename T>
std::basic_string<T> vformat(T const *format, va_list args);

Et la source (stdiomm.cpp):

#include "stdiomm.h"
#include <boost/scoped_array.hpp>
#include <cstdio>

template <>
std::wstring vformat(wchar_t const *format, va_list arguments)
{
#if defined(_WIN32)
    int required(_vscwprintf(format, arguments));
    assert(required >= 0);
    boost::scoped_array<wchar_t> buffer(new wchar_t[required + 1]);
    int written(vswprintf(buffer.get(), required + 1, format, arguments));
    assert(written == required);
    return std::wstring(buffer.get(), written);
#else
#   error "No implementation yet"
#endif
}

template <>
std::string vformat(char const *format, va_list arguments)
{
#if defined(_WIN32)
    int required(_vscprintf(format, arguments));
    assert(required >= 0);
    boost::scoped_array<char> buffer(new char[required + 1]);
    int written(vsnprintf(buffer.get(), required + 1, format, arguments));
    assert(written == required);
    return std::string(buffer.get(), written);
#else
    char *buffer;
    int printed = vasprintf(&buffer, format, arguments);
    assert(printed != -1);
    std::string retval(buffer, printed);
    free(buffer);
    return retval;      
#endif
}

template <typename T>
std::basic_string<T> format(T const *format, ...)
{
    va_list ap;
    va_start(ap, format);
    std::basic_string<T> retval(vformat(format, ap));
    va_end(ap);
    return retval;
}

template std::wstring format(wchar_t const *format, ...);
template std::string format(char const *format, ...);

Mettre à jour

Après avoir lu certaines des réponses, je devrais peut-être passer à boost::format() moi-même!

3
Matt Joiner

Même si la question est plutôt ancienne, je veux ajouter mes deux sous.

Imprimer des objets créés par l'utilisateur avec printf ()

Ceci est assez simple si vous y réfléchissez - vous pouvez modifier votre type et envoyer la chaîne à printf:

std::string to_string(const MyClass &x)
{
     return to_string(x.first)+" "+to_string(x.second);
}

//...

printf("%s is awesome", to_string(my_object).c_str()); //more or less

Dommage qu'il n'y en ait pas (il y a C++ 11 to_string ()), une interface C++ standardisée pour stringifier les objets ...

pitf printf ()

Un seul drapeau -% n

Le seul qui est un paramètre de sortie - il attend un pointeur sur int. Il écrit le nombre de caractères écrits avec succès sur l'emplacement indiqué par ce pointeur. Son utilisation habile peut provoquer un dépassement de capacité, ce qui constitue une vulnérabilité de la sécurité (voir Attaque par la chaîne de format printf ()).

1
milleniumbug

Vous pouvez obtenir le meilleur des deux mondes avec la bibliothèque fmt qui associe la sécurité et l’extensibilité de iostreams à la convivialité et aux performances de (s)printf. Exemple:

std::string = fmt::format("The answer is {}", 42);

La bibliothèque prend en charge la syntaxe des chaînes de format Python et printf.

Disclaimer: Je suis l'auteur de la bibliothèque fmt .

1
vitaut

Ça dépend de la situation. Rien n'est parfait. J'utilise les deux. Les flux sont utiles pour les types personnalisés car vous pouvez surcharger l'opérateur >> dans ostream. Mais quand il s'agit d'espacement, il est préférable d'utiliser printf (). stringstream et like sont meilleurs que le style C strcat (). Alors utilisez-en un qui convient à la situation.

0
quantum

C++ streams sont surestimés, après tout, ils ne sont en fait que des classes avec un opérateur surchargé <<.
J'ai souvent lu que les flux sont du type C++ comme printf est du type C , mais qu'ils sont tous deux des fonctionnalités de bibliothèque disponibles en C++, vous devez donc utiliser ce qui convient le mieux.
Je préfère surtout printf, mais j'ai également utilisé des flux, qui fournissent un code plus propre et vous évitent d'avoir à faire correspondre% placeholders à des arguments.

0
Petruza

J'ai lu des avertissements disant que cout et cerr sont dangereux pour le multithreading. Si c'est vrai, c'est une bonne raison d'éviter de les utiliser. Note: J'utilise GNU g ++ avec openMP.

0
Oleg Shalaev

J'utilise presque toujours printf pour les instructions de débogage temporaires. Pour un code plus permanent, je préfère les flux "c" tels qu'ils sont The C++ Way . Même si boost :: format semble prometteur et pourrait remplacer l’utilisation de mon flux (en particulier pour les sorties au format complexe), rien ne remplacera probablement printf pour moi pendant longtemps.

0
aib