Question

Dans le nouveau code C ++, j'ai tendance à utiliser la bibliothèque C ++ iostream au lieu de la bibliothèque C stdio.

J'ai remarqué que certains programmeurs semblent s'en tenir à la stdio, insistant sur le fait qu'il est plus portable.

Est-ce vraiment le cas? Quoi de mieux à utiliser?

Était-ce utile?

La solution

Pour répondre à la question d'origine:
Tout ce qui peut être fait en utilisant stdio peut être fait en utilisant la bibliothèque iostream.

Disadvantages of iostreams: verbose
Advantages    of iostreams: easy to extend for new non POD types.

L’avancée du C ++ sur C était le type de sécurité.

  • iostreams a été conçu pour pouvoir taper de manière explicite. Ainsi, l'affectation à un objet vérifiait explicitement le type (au moment du compilateur) de l'objet affecté (générant éventuellement une erreur de temps de compilation). Ainsi, évitez les surcharges de la mémoire d'exécution ou l'écriture d'une valeur float dans un objet char, etc.

  • scanf () / printf () et sa famille comptent sur le programmeur pour obtenir la chaîne de formatage correcte et qu'il n'y a pas eu de vérification de type (je pense que gcc a une extension qui aide). En conséquence, il a été la source de nombreux bugs (car les programmeurs sont moins parfaits dans leur analyse que les compilateurs [ne diront pas que les compilateurs sont parfaits, mais meilleurs que les humains]).

Juste pour clarifier les commentaires de Colin Jensen.

  • Les bibliothèques iostream sont stables depuis la publication de la dernière norme (j’oublie l’année actuelle mais il ya environ 10 ans).

Clarifier les commentaires de Mikael Jansson.

  • Les autres langues citées dans le style de format, qu'il mentionne, disposent de protections explicites pour éviter les dangereux effets secondaires de la bibliothèque C stdio qui peuvent (en C mais pas les langues mentionnées) provoquer un blocage à l'exécution.

N.B. Je conviens que la bibliothèque iostream est un peu verbeuse. Mais je suis prêt à supporter la verbosité pour assurer la sécurité de l'exécution. Mais nous pouvons atténuer la verbosité en utilisant Bibliothèque de formats Boost .

#include <iostream>
#include <iomanip>
#include <boost/format.hpp>

struct X
{  // this structure reverse engineered from
   // example provided by 'Mikael Jansson' in order to make this a running example

    char*       name;
    double      mean;
    int         sample_count;
};
int main()
{
    X   stats[] = {{"Plop",5.6,2}};

    // nonsense output, just to exemplify

    // stdio version
    fprintf(stderr, "at %p/%s: mean value %.3f of %4d samples\n",
            stats, stats->name, stats->mean, stats->sample_count);

    // iostream
    std::cerr << "at " << (void*)stats << "/" << stats->name
              << ": mean value " << std::fixed << std::setprecision(3) << stats->mean
              << " of " << std::setw(4) << std::setfill(' ') << stats->sample_count
              << " samples\n";

    // iostream with boost::format
    std::cerr << boost::format("at %p/%s: mean value %.3f of %4d samples\n")
                % stats % stats->name % stats->mean % stats->sample_count;
}

Autres conseils

C'est trop verbeux.

Réfléchissez à la structure iostream pour effectuer les opérations suivantes (de la même manière pour scanf):

// nonsense output, just to examplify
fprintf(stderr, "at %p/%s: mean value %.3f of %4d samples\n",
    stats, stats->name, stats->mean, stats->sample_count);

Cela nécessite quelque chose comme:

std::cerr << "at " << static_cast<void*>(stats) << "/" << stats->name
          << ": mean value " << std::precision(3) << stats->mean
          << " of " << std::width(4) << std::fill(' ') << stats->sample_count
          << " samples " << std::endl;

Le formatage de chaîne est un cas où l'orientation orientée objet peut et doit être annulée en faveur d'un DSL de formatage incorporé dans des chaînes. Pensez au format Lisp, au formatage printf-style de Python, ou à PHP, Bash, Perl, Ruby et à leurs intégrations de chaînes.

iostream car, dans le meilleur des cas, ce cas d'utilisation est erroné.

La Format Boost Library offre une alternative orientée objet et respectueuse du type pour le formatage de chaîne de style printf. Elle constitue un complément à iostream qui ne souffre pas des problèmes de verbosité habituels dus à l'utilisation intelligente de l'opérateur%. Je vous recommande de ne pas utiliser plain C printf si vous n’aimez pas le formatage avec l’opérateur de iostream & Lt; & Lt;.

.

De retour au bon vieux temps, le comité de normalisation C ++ continuait de bricoler avec le langage et iostreams était une cible mouvante. Si vous utilisiez iostreams, vous avez ensuite eu la possibilité de réécrire des parties de votre code chaque année. Pour cette raison, j’ai toujours utilisé la stdio qui n’a pas beaucoup changé depuis 1989.

Si je faisais des choses aujourd'hui, j'utiliserais iostreams.

Si, comme moi, vous avez appris le C avant le C ++, les bibliothèques stdio semblent plus naturelles à utiliser. Il existe des avantages et des inconvénients entre iostream et stdio, mais printf () ne me manque pas lorsque j'utilise iostream.

En principe, j’utiliserais iostreams. En pratique, j’ai trop de décimales formatées, etc., ce qui rend iostream trop illisible. J’utilise donc stdio. Boost :: Le format est une amélioration, mais pas assez motivant pour moi. En pratique, stdio est presque digne de ce nom puisque la plupart des compilateurs modernes vérifient quand même les arguments.

C'est un domaine dans lequel je ne suis toujours pas totalement satisfait des solutions.

Pour les entrées / sorties binaires, j’ai tendance à utiliser les commandes fread et fwrite de stdio. Pour les éléments formatés, je vais généralement utiliser IO Stream bien que, comme l'a dit Mikael, le formatage non-trival (non-default?) Puisse être un PITA.

Je vais comparer les deux principales bibliothèques de la bibliothèque standard C ++.

Vous ne devriez pas utiliser les routines de traitement de chaîne basées sur C-style-format-string en C ++.

Il existe plusieurs raisons pour limiter leur utilisation:

  • Pas de type sécurité
  • Vous ne pouvez pas transmettre de types non-POD à des listes d’arguments variadiques (c’est-à-dire ni à scanf + co., ni à printf + co.), ou vous entrez dans la forteresse sombre du comportement indéfini
  • Facile de se tromper:
    • Vous devez réussir à conserver la chaîne de format et la " liste d'arguments de valeur " en synchronisation
    • Vous devez rester synchronisé correctement

Bugs subtils introduits dans des endroits éloignés

Ce n’est pas seulement le printf en lui-même qui n’est pas bon. Les logiciels vieillissent, sont refactorisés et modifiés, et des erreurs peuvent être introduites à partir d'endroits distants. Supposons que vous ayez

.

// foo.h
...
float foo;
...

et quelque part ...

// bar/frob/42/icetea.cpp
...
scanf ("%f", &foo);
...

Et trois ans plus tard, vous trouvez que foo devrait être d'un type personnalisé ...

// foo.h
...
FixedPoint foo;
...

mais quelque part ...

  printf ("My Matrix: %f %f %f %f\n"
          "           %f %f %f %f\n"
          "           %f %f %f %f\n"
          "           %f %f %f %f\n",
          mat(0,0), mat(0,1), mat(0,2), mat(0,3), 
          mat(1,0), mat(1,1), mat(1,2), mat(1,3), 
          mat(2,0), mat(2,1), mat(2,2), mat(2,3), 
          mat(3,0), mat(3,1), mat(3,2), mat(3,3));

... alors votre ancien printf / scanf sera toujours compilé, sauf que vous obtenez maintenant des segfaults aléatoires et vous ne vous souvenez plus pourquoi.

Verbosité des iostreams

Si vous pensez que printf () est moins détaillé, il est fort probable que vous n'utilisiez pas toute la puissance de leur iostream. Exemple:

cout << mat << '\n';

Comparez cela à l'utilisation appropriée d'iostreams:

printf ("Guten Morgen, Sie sind %f Meter groß und haben %d Kinder", 
        someFloat, someInt);

printf ("Good morning, you have %d children and your height is %f meters",
        someFloat, someInt); // Note: Position changed.

// ^^ not the best example, but different languages have generally different
//    order of "variables"

Vous devez définir une surcharge appropriée pour l'opérateur < < qui a à peu près la structure de la printf-thingy, mais la différence importante est que vous avez maintenant quelque chose de réutilisable et de typé; Bien sûr, vous pouvez aussi créer quelque chose de réutilisable pour les goûts de printf, mais vous aurez alors printf à nouveau (que se passera-t-il si vous remplacez les membres de la matrice par le nouveau FixedPoint?), mis à part d'autres éléments non triviaux, par exemple. vous devez transmettre FILE * handles autour.

Les chaînes de format de style C ne sont pas meilleures pour I18N que iostreams

Notez que les chaînes de format sont souvent considérées comme le sauvetage de l'internationalisation, mais elles ne sont pas du tout meilleures que iostream à cet égard:

cout << format("%1% %2% %3% %2% %1% \n") % "11" % "22" % "333"; // 'simple' style.

C'est-à-dire que les anciennes chaînes de format C n'ont pas autant d'informations de position que les iostreams.

Vous pouvez envisager de boost :: format , qui offre un support pour indiquer explicitement la position dans la chaîne de format. De leur section exemples:

shared_ptr<float> f(new float);
fscanf (stdout, "%u %s %f", f)

Certaines implémentations de printf fournissent des arguments de position, mais ils ne sont pas standard.

Dois-je ne jamais utiliser des chaînes de format de style C?

À part la performance (comme l'a souligné Jan Hudec), je ne vois pas de raison. Mais gardez à l'esprit:

  

& # 8220; Nous devrions oublier les petites efficacités, disons environ 97% du temps: l'optimisation prématurée est la racine de tout mal. Pourtant, nous ne devrions pas laisser passer nos opportunités dans ces 3% critiques. Un bon programmeur ne se laissera pas aller à la complaisance par un tel raisonnement, il sera sage de regarder attentivement le code critique; mais seulement après que ce code a été identifié & # 8221; - Knuth

et

  

& # 8220; Les goulots d'étranglement se produisent dans des endroits surprenants, alors n'essayez pas de vous faire une idée de ce qu'il en est et de faire un piratage de vitesse tant que vous n'avez pas prouvé que c'est là que se trouve le goulot d'étranglement. - Brochet

Oui, les implémentations de printf sont généralement plus rapides que celles de iostream que boost :: format (à partir d’un benchmark spécifique que j’ai écrit, mais il devrait dépendre en grande partie de la situation en particulier: si printf = 100%, iostream = 160% et boost :: format = 220%)

Mais n'omettez pas aveuglément d'y penser: combien de temps passez-vous sur le traitement de texte? Combien de temps votre programme s'exécute-t-il avant de quitter? Est-il pertinent de se rabattre sur les chaînes de format C-style, la sécurité en vrac, la diminution de la ré-adsorbabilité, augmenter la probabilité de bugs très subtilespeut se cacher pendant des années et peut seulement se révéler juste dans vos clients favoris face?

Personnellement, je ne reculerais pas si je ne pouvais pas gagner plus de 20% d’accélération. Mais parce que mes applications Je passe plus de temps que jamais à traiter des chaînes de caractères. Quelques analyseurs J'ai écrit passer pratiquement tout leur temps sur le traitement des chaînes, mais leur exécution totale est si petite qu'il ne vaut pas la peine d'être testé et vérifié.

Quelques énigmes

Enfin, j'aimerais définir certaines énigmes:

Trouvez toutes les erreurs, car le compilateur ne le fera pas (il ne peut suggérer que s'il est gentil):

const char *output = "in total, the thing is 50%"
                     "feature  complete";
printf (output);

Si rien d'autre, qu'est-ce qui ne va pas avec celui-ci?

<*>

Bien que l’API C ++ iostreams présente de nombreux avantages, l’un des problèmes les plus importants concerne l’i18n. Le problème est que l'ordre des substitutions de paramètres peut varier en fonction de la culture. L'exemple classique est quelque chose comme:

// i18n UNSAFE 
std::cout << "Dear " << name.given << ' ' << name.family << std::endl;

Bien que cela fonctionne pour l'anglais, en chinois, le nom de famille vient en premier.

En ce qui concerne la traduction de votre code pour les marchés étrangers, la traduction d'extraits de code est une tâche périlleuse, de sorte que les nouvelles l10ns peuvent nécessiter des modifications du code et pas uniquement des chaînes différentes.

boost :: format semble combiner le meilleur de stdio (une chaîne de format unique pouvant utiliser les paramètres dans un ordre différent de celui où ils apparaissent) et iostreams (sécurité du type, extensibilité).

J'utilise iostreams, principalement parce que cela facilite la manipulation ultérieure du flux (si j'en ai besoin). Par exemple, vous pouvez savoir que vous souhaitez afficher la sortie dans une fenêtre de trace - cela est relativement facile à faire avec cout et cerr. Bien sûr, vous pouvez jouer avec des pipes et des choses sous Unix, mais ce n’est pas aussi portable.

J'aime beaucoup le formatage printf. Je commence par conséquent par formater une chaîne, puis à l'envoyer dans la mémoire tampon. Avec Qt, j’utilise souvent QString :: sprintf (bien qu'ils recommandent d'utiliser < a href = "http://doc.trolltech.com/4.4/qstring.html#arg-10" rel = "nofollow noreferrer"> QString :: arg à la place). J'ai consulté boost.format . aussi, mais ne pouvait pas vraiment s'habituer à la syntaxe (trop de%). Je devrais toutefois y jeter un coup d'œil.

Ce qui me manque dans iolibraries, c’est l’entrée formatée.

iostreams n’a pas un moyen agréable de répliquer scanf () et même boost n’a pas l’extension requise pour l’entrée.

stdio est préférable pour lire des fichiers binaires (comme des blocs de freading dans un vecteur < unsigned char > et en utilisant .resize (), etc.). Voir la fonction read_rest dans file.hh dans http://nuwen.net/libnuwen.html pour un an. exemple.

Les flux C ++ peuvent saturer de nombreux octets lors de la lecture de fichiers binaires, ce qui provoque un faux eof.

Puisque les iostreams sont devenus un standard, vous devez les utiliser en sachant que votre code fonctionnera à coup sûr avec les versions les plus récentes du compilateur. Je suppose qu’aujourd’hui, la plupart des compilateurs connaissent très bien iostreams et qu’ils ne devraient pas rencontrer de problèmes pour les utiliser.

Mais si vous voulez vous en tenir aux fonctions * printf, il ne peut y avoir de problème à mon avis.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top