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
?
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++.
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 .
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.
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");
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.
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.
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.
// 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.
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.
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.
J'utilise printf parce que je déteste la syntaxe laide <<cout<<
.
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.
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.
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.
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.
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.
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.
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, ...);
Après avoir lu certaines des réponses, je devrais peut-être passer à boost::format()
moi-même!
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 ()).
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 .
Ç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.
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.
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.
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.