Domanda

Nel nuovo codice C ++, tendo a usare la libreria iostream C ++ anziché la libreria C stdio.

Ho notato che alcuni programmatori sembrano attenersi allo stdio, insistendo sul fatto che è più portatile.

È davvero così? Cosa è meglio usare?

È stato utile?

Soluzione

Per rispondere alla domanda originale:
Tutto ciò che può essere fatto usando stdio può essere fatto usando la libreria iostream.

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

Il passo avanti fatto dal C ++ su C è stato la sicurezza dei tipi.

  • iostreams è stato progettato per essere esplicitamente sicuro. Pertanto, l'assegnazione a un oggetto ha verificato esplicitamente anche il tipo (al momento del compilatore) dell'oggetto assegnato (generando un errore in fase di compilazione, se necessario). In questo modo evita il sovraccarico della memoria di runtime o la scrittura di un valore float su un oggetto char ecc.

  • scanf () / printf () e la famiglia, d'altra parte, si affidano al programmatore che ottiene la stringa di formato corretta e non c'è stato alcun controllo del tipo (credo che gcc abbia un'estensione che aiuta). Di conseguenza è stata la fonte di molti bug (poiché i programmatori sono meno perfetti nella loro analisi rispetto ai compilatori [non dirà che i compilatori sono perfetti solo meglio degli umani]).

Solo per chiarire i commenti di Colin Jensen.

  • Le librerie iostream sono rimaste stabili dal rilascio dell'ultimo standard (dimentico l'anno effettivo ma circa 10 anni fa).

Per chiarire i commenti di Mikael Jansson.

  • Le altre lingue che menziona che usano lo stile del formato hanno esplicite garanzie per prevenire i pericolosi effetti collaterali della libreria C stdio che possono (in C ma non nelle lingue menzionate) causare un arresto anomalo del runtime.

N.B. Sono d'accordo sul fatto che la libreria iostream sia un po 'prolissa. Ma sono disposto a tollerare la verbosità per garantire la sicurezza in fase di esecuzione. Ma possiamo mitigare la verbosità usando Boost Format Library .

#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;
}

Altri suggerimenti

È troppo prolisso.

Rifletti sul costrutto iostream per fare quanto segue (analogamente per 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);

Ciò richiederebbe qualcosa del tipo:

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;

La formattazione delle stringhe è un caso in cui l'orientamento agli oggetti può e dovrebbe essere evitato a favore di una formattazione DSL incorporata nelle stringhe. Prendi in considerazione format, la formattazione in stile printf di Python o PHP, Bash, Perl, Ruby e la loro intrapolazione di stringhe.

iostream per quel caso d'uso non è corretto, nella migliore delle ipotesi.

Il Boost Format Library offre un'alternativa sicura e orientata agli oggetti per la formattazione di stringhe in stile printf ed è un complemento agli iostreams che non soffrono dei soliti problemi di verbosità a causa dell'uso intelligente dell'operatore%. Ti consiglio di prenderlo in considerazione usando la semplice stampa C se non ti piace la formattazione con l'operatore di iostream & Lt; & Lt ;.

Ai vecchi tempi, il comitato per gli standard del C ++ continuava a scherzare con il linguaggio e gli iostreams erano un bersaglio mobile. Se hai usato iostreams, ti è stata data l'opportunità di riscrivere parti del tuo codice ogni anno o giù di lì. Per questo motivo, ho sempre usato lo stdio che non è cambiato in modo significativo dal 1989.

Se oggi facessi cose, userei iostreams.

Se, come me, hai imparato C prima di imparare C ++, le librerie stdio sembrano più naturali da usare. Ci sono pro e contro per iostream vs stdio ma mi manca printf () quando uso iostream.

In linea di principio userei iostreams, in pratica faccio troppi decimali formattati, ecc. che rendono gli iostreams troppo illeggibili, quindi uso stdio. Boost :: format è un miglioramento, ma non abbastanza motivante per me. In pratica, stdio è quasi dilemma poiché la maggior parte dei compilatori moderni fa comunque il controllo degli argomenti.

È un'area in cui non sono ancora del tutto soddisfatto di nessuna delle soluzioni.

Per l'IO binario, tendo ad usare la fread e il fwrite di stdio. Per cose formattate di solito userò IO Stream anche se, come ha detto Mikael, la formattazione non trival (non predefinita?) Può essere una PITA.

Paragonerò le due librerie mainstream dalla libreria standard C ++.

Non dovresti usare le routine di elaborazione delle stringhe basate su stringhe in stile C in C ++.

Esistono diversi motivi per mitigarne l'uso:

  • Non tipesa
  • Non è possibile passare tipi non POD agli elenchi di argomenti variadici (ovvero, né a scanf + co., né a printf + co.), oppure entri nella Dark Stronghold of Undefined Behaviour
  • Facile da sbagliare:
    • Devi riuscire a mantenere la stringa di formato e il " value-argomento-elenco " in sincronia
    • Devi sincronizzare correttamente

Bug sottili introdotti in luoghi remoti

Non è solo la stampa in sé che non è buona. Il software invecchia, viene refactored e modificato e potrebbero essere introdotti errori da postazioni remote. Supponi di avere

.

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

e da qualche parte ...

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

E tre anni dopo scopri che il foo dovrebbe essere di qualche tipo personalizzato ...

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

ma da qualche parte ...

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

... allora il tuo vecchio printf / scanf verrà comunque compilato, tranne per il fatto che ora ricevi segfault casuali e non ricordi il perché.

Verbosità di iostreams

Se pensi che printf () sia meno dettagliato, allora c'è una certa probabilità che non usi tutta la forza dei loro iostream. Esempio:

cout << mat << '\n';

Confrontalo con l'utilizzo di iostreams giusto:

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"

Devi definire un sovraccarico adeguato per l'operatore < < che ha all'incirca la struttura del printf-thingy, ma la differenza significativa è che ora hai qualcosa di riutilizzabile e di sicurezza; ovviamente puoi anche rendere riutilizzabile qualcosa di simile a printf-like, ma poi hai di nuovo printf (e se sostituissi i membri della matrice con il nuovo FixedPoint?), a parte altre non banalità, ad es. devi passare le maniglie FILE * in giro.

Le stringhe in formato C non sono migliori per I18N rispetto agli iostreams

Si noti che le stringhe di formato sono spesso pensate come il salvataggio con l'internazionalizzazione, ma non sono affatto migliori di iostream in questo senso:

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

Ad esempio, le stringhe di vecchio formato C in stile mancano di informazioni sulla posizione tanto quanto gli iostreams.

Potresti prendere in considerazione boost :: format , che offre supporto per indicare esplicitamente la posizione nella stringa di formato. Dalla loro sezione di esempi:

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

Alcune implementazioni di printf forniscono argomenti posizionali, ma non sono standard.

Non dovrei mai usare stringhe in formato C?

A parte la performance (come sottolineato da Jan Hudec), non vedo un motivo. Ma tieni a mente:

  

& # 8220; Dovremmo dimenticare le piccole efficienze, diciamo circa il 97% delle volte: l'ottimizzazione prematura è la radice di tutti i mali. Eppure non dovremmo rinunciare alle nostre opportunità in quel 3% critico. Un buon programmatore non si crogiolerà nella compiacenza di tale ragionamento, sarà saggio guardare attentamente il codice critico; ma solo dopo che quel codice è stato identificato & # 8221; - Knuth

e

  

& # 8220; I colli di bottiglia si verificano in luoghi sorprendenti, quindi non provare a indovinare e metti un trucco per la velocità fino a quando non hai provato che è lì che si trova il collo di bottiglia. & # 8221; - Pike

Sì, le implementazioni di printf sono in genere più veloci di iostreams in genere più veloci di boost :: format (da un benchmark piccolo e specifico che ho scritto, ma dovrebbe dipendere in gran parte dalla situazione in particolare: se printf = 100%, allora iostream = 160% e boost :: format = 220%)

Ma non omettere ciecamente di pensarci: quanto tempo davvero dedichi all'elaborazione del testo? Quanto dura il programma prima di uscire? È rilevante ricorrere alle stringhe in formato C, sicurezza allentata, ridurre la rifrattorbilità, aumentare la probabilità di bug molto sottili chepuò nascondersi per anni e può rivelarsi giusto nei tuoi clienti preferiti di fronte?

Personalmente, non potrei tornare indietro se non riesco a guadagnare più del 20% di velocità. Ma perché le mie applicazioni trascorro praticamente tutto il loro tempo in attività diverse dall'elaborazione delle stringhe, che non ho mai dovuto fare. Alcuni parser Ho scritto di spendere praticamente tutto il loro tempo nell'elaborazione delle stringhe, ma il loro runtime totale è così piccolo che non vale lo sforzo di test e verifica.

Alcuni enigmi

Infine, vorrei preimpostare alcuni enigmi:

Trova tutti gli errori, perché il compilatore non lo farà (può solo suggerire se è simpatico):

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

Se non altro, cosa c'è di sbagliato in questo?

<*>

Sebbene ci siano molti vantaggi nell'API iostreams C ++, un problema significativo è che riguarda i18n. Il problema è che l'ordine delle sostituzioni dei parametri può variare in base alla cultura. L'esempio classico è qualcosa del tipo:

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

Mentre funziona per l'inglese, in cinese il nome della famiglia viene per primo.

Quando si tratta di tradurre il codice per i mercati esteri, la traduzione di frammenti è piena di pericoli, quindi i nuovi l10ns potrebbero richiedere modifiche al codice e non solo stringhe diverse.

boost :: format sembra combinare il meglio di stdio (una stringa di singolo formato che può usare i parametri in un ordine diverso da quello che appaiono) e iostreams (sicurezza del tipo, estensibilità).

Uso gli iostreams, principalmente perché ciò rende più facile giocherellare con il flusso in seguito (se ne ho bisogno). Ad esempio, potresti scoprire che vuoi visualizzare l'output in qualche finestra di traccia - questo è relativamente facile da fare con cout e cerr. Ovviamente puoi armeggiare con pipe e roba su unix, ma non è portatile.

Adoro la formattazione simile a printf, quindi di solito formatto prima una stringa e quindi la invio al buffer. Con Qt, utilizzo spesso QString :: sprintf (anche se mi consiglia di utilizzare < a href = "http://doc.trolltech.com/4.4/qstring.html#arg-10" rel = "nofollow noreferrer"> QString :: arg invece). Ho visto boost.format anche, ma non potevo davvero abituarmi alla sintassi (troppe% 's). Dovrei davvero dare un'occhiata, però.

Quello che mi manca degli iolibrari è l'input formattato.

iostreams non ha un buon modo per replicare scanf () e persino boost non ha l'estensione richiesta per l'input.

stdio è meglio per leggere file binari (come blocchi di frange in un vettore < unsigned char > e usando .resize () ecc.). Vedi la funzione read_rest in file.hh in http://nuwen.net/libnuwen.html per un esempio.

I flussi C ++ possono soffocare su molti byte durante la lettura di file binari causando un falso eof.

Dato che gli iostreams sono diventati uno standard, dovresti usarli sapendo che il tuo codice funzionerà sicuramente con le versioni più recenti del compilatore. Immagino che al giorno d'oggi la maggior parte dei compilatori sappia molto bene di iostreams e non ci dovrebbero essere problemi ad usarli.

Ma se vuoi restare fedele alle funzioni di * printf, secondo me non ci possono essere problemi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top