'printf' vs. 'cout' in C ++


Risposte:


333

Sono sorpreso che tutti in questa domanda affermino che std::coutè molto meglio di printf, anche se la domanda ha solo chiesto differenze. Ora, c'è una differenza - std::coutè C ++, ed printfè C (tuttavia, è possibile utilizzarlo in C ++, proprio come quasi ogni altra cosa da C). Ora, sarò onesto qui; entrambi printfe std::couthanno i loro vantaggi.

Differenze reali

Estensibilità

std::coutè estensibile. So che la gente dirà che printfè anche estensibile, ma tale estensione non è menzionata nello standard C (quindi dovresti usare funzionalità non standard - ma non esiste nemmeno una funzionalità non standard comune), e tali estensioni sono una lettera (quindi è facile entrare in conflitto con un formato già esistente).

Diversamente printf, std::coutdipende completamente dal sovraccarico dell'operatore, quindi non ci sono problemi con i formati personalizzati: tutto ciò che devi fare è definire una subroutine std::ostreamcome primo argomento e il tuo tipo come secondo. Come tale, non ci sono problemi di spazio dei nomi - finché hai una classe (che non è limitata a un carattere), puoi avere un std::ostreamsovraccarico di lavoro per questo.

Tuttavia, dubito che molte persone vorrebbero estendere ostream(ad essere onesti, raramente ho visto tali estensioni, anche se sono facili da realizzare). Tuttavia, è qui se ne hai bisogno.

Sintassi

Come potrebbe essere facilmente notare, sia printfe std::coututilizzare sintassi diversa. printfusa la sintassi delle funzioni standard usando la stringa di pattern e gli elenchi di argomenti a lunghezza variabile. In realtà, printfè un motivo per cui C li ha: i printfformati sono troppo complessi per essere utilizzabili senza di essi. Tuttavia, std::coututilizza un'API diversa: l' operator <<API che restituisce se stessa.

In genere, ciò significa che la versione C sarà più corta, ma nella maggior parte dei casi non importa. La differenza si nota quando si stampano molti argomenti. Se devi scrivere qualcosa del genere Error 2: File not found., assumendo il numero di errore e la sua descrizione è segnaposto, il codice sarebbe simile a questo. Entrambi gli esempi funzionano in modo identico (beh, in qualche modo, std::endleffettivamente svuota il buffer).

printf("Error %d: %s.\n", id, errors[id]);
std::cout << "Error " << id << ": " << errors[id] << "." << std::endl;

Anche se questo non sembra troppo folle (è solo due volte più lungo), le cose diventano più folli quando si formattano gli argomenti, invece di stamparli. Ad esempio, la stampa di qualcosa di simile 0x0424è semplicemente pazza. Ciò è causato dalla std::coutmiscelazione dello stato e dei valori effettivi. Non ho mai visto un linguaggio in cui qualcosa del genere std::setfillsarebbe un tipo (diverso dal C ++, ovviamente). printfsepara chiaramente argomenti e tipo reale. Preferirei davvero mantenerne la printfversione (anche se sembra un po 'criptica) rispetto alla iostreamversione di essa (poiché contiene troppo rumore).

printf("0x%04x\n", 0x424);
std::cout << "0x" << std::hex << std::setfill('0') << std::setw(4) << 0x424 << std::endl;

Traduzione

Questo è il vero vantaggio delle printfbugie. La printfstringa di formato è bene ... una stringa. Ciò rende davvero facile la traduzione, rispetto operator <<all'abuso di iostream. Supponendo che la gettext()funzione traduca e si desideri mostrare Error 2: File not found., il codice per ottenere la traduzione della stringa di formato mostrata in precedenza sarebbe simile al seguente:

printf(gettext("Error %d: %s.\n"), id, errors[id]);

Supponiamo ora di tradurre in Fictionish, dove il numero di errore si trova dopo la descrizione. La stringa tradotta sarebbe simile %2$s oru %1$d.\n. Ora, come si fa in C ++? Bene, non ne ho idea. Immagino che tu possa falsificare iostreamquali costrutti printfpuoi passare a gettext, o qualcosa, ai fini della traduzione. Certo, $non è lo standard C, ma è così comune che a mio avviso è sicuro da usare.

Non è necessario ricordare / cercare la sintassi specifica del tipo intero

C ha molti tipi interi, così come C ++. std::coutgestisce tutti i tipi per te, mentre printfrichiede una sintassi specifica a seconda di un tipo intero (ci sono tipi non interi, ma l'unico tipo non intero che userai in pratica con printfè const char *(stringa C, che può essere ottenuta usando il to_cmetodo di std::string)). Ad esempio, per stampare size_t, è necessario utilizzare %zd, mentre int64_trichiederà l'utilizzo %"PRId64". Le tabelle sono disponibili su http://en.cppreference.com/w/cpp/io/c/fprintf e http://en.cppreference.com/w/cpp/types/integer .

Non è possibile stampare il byte NUL, \0

Poiché printfutilizza le stringhe C anziché le stringhe C ++, non può stampare byte NUL senza trucchi specifici. In alcuni casi è possibile utilizzare %ccon '\0'come argomento, anche se questo è chiaramente un hack.

Differenze che non interessano a nessuno

Prestazione

Aggiornamento: si scopre che iostreamè così lento che di solito è più lento del disco rigido (se si reindirizza il programma su file). La disabilitazione della sincronizzazione con stdiopuò essere d'aiuto, se è necessario produrre molti dati. Se la performance è una vera preoccupazione (invece di scrivere più righe su STDOUT), basta usare printf.

Tutti pensano che si preoccupino delle prestazioni, ma nessuno si preoccupa di misurarle. La mia risposta è che l'I / O è comunque un collo di bottiglia, non importa se lo usi printfo iostream. Penso che printf potrebbe essere più veloce da una rapida occhiata in assembly (compilato con clang usando l' -O3opzione del compilatore). Supponendo il mio esempio di errore, l' printfesempio fa meno chiamate rispetto coutall'esempio. Questo è int maincon 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:

Si può facilmente notare che due stringhe e 2(numero) vengono spinti come printfargomenti. Questo è tutto; non c'è nient'altro. Per confronto, questo viene iostreamcompilato per l'assemblaggio. No, non c'è allineamento; ogni singola operator <<chiamata indica un'altra chiamata con un'altra serie di argomenti.

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:

Tuttavia, a dire il vero, questo non significa nulla, dato che l'I / O è comunque il collo di bottiglia. Volevo solo dimostrare che iostreamnon è più veloce perché è "sicuro da scrivere". La maggior parte delle implementazioni in C implementa i printfformati usando goto calcolato, quindi printfè il più veloce possibile, anche senza che il compilatore ne sia a conoscenza printf(non che non lo siano - alcuni compilatori possono ottimizzare printfin alcuni casi - la stringa costante che termina con \nè di solito ottimizzata per puts) .

Eredità

Non so perché vorresti ereditare ostream, ma non mi interessa. È possibile FILEanche con .

class MyFile : public FILE {}

Digitare sicurezza

I veri elenchi di argomenti a lunghezza variabile non hanno sicurezza, ma non importa, poiché i compilatori C più diffusi possono rilevare problemi con la printfstringa di formato se si abilitano gli avvisi. In effetti, Clang può farlo senza abilitare gli avvisi.

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

18
Dici che l'I / O è comunque il collo di bottiglia. Ovviamente non hai mai provato questo presupposto. Cito me stesso: "D'altra parte, la versione di iostreams, a 75.3 MB / s, non può bufferizzare i dati abbastanza velocemente da stare al passo con un disco rigido. È un male, e non sta ancora facendo un vero lavoro. credo di avere aspettative troppo alte quando dico che la mia libreria I / O dovrebbe essere in grado di saturare il mio controller del disco. "
Ben Voigt,

4
@BenVoigt: lo ammetto, cerco di evitare C ++ quando possibile. Ho provato ad usarlo molto, ma era più fastidioso e meno gestibile rispetto ad altri linguaggi di programmazione che ho usato. Questa è un'altra ragione per cui evito il C ++ - non è nemmeno veloce (non è nemmeno iostream - l'intera libreria C ++ è lenta nella maggior parte delle implementazioni, forse con l'eccezione di std::sort, che è in qualche modo sorprendentemente veloce rispetto a qsort(2 volte), a costo di dimensione eseguibile).
Konrad Borowski,

3
Nessuno qui ha menzionato problemi in ambiente parallelo quando si utilizza cout.
Nicholas Hamilton,

9
Il tuo argomento di prestazione non ha alcun senso. Altro assemblaggio nel vostro programma non vuol dire che il programma sarà più lento, perché si sta non rappresentano tutto il codice che fa la funzione printf, che è un sacco di codice. A mio avviso, è possibile ottimizzare cout con << operatore molto meglio di printf, perché il compilatore può avere un migliore senso delle variabili e della formattazione.
Ignas2526,

18
Mi piace un sacco di cose su questa risposta, ma forse la mia parte preferita è "Tutti pensano che si preoccupino delle prestazioni, ma nessuno si preoccupa di misurarla".
Kyle Strand,

203

Dalle domande frequenti su C ++ :

[15.1] Perché dovrei usare al <iostream> posto del tradizionale <cstdio>?

Aumentare la sicurezza dei tipi, ridurre gli errori, consentire l'estensibilità e fornire l'ereditarietà.

printf()è probabilmente non rotto, ed scanf()è forse vivibile nonostante sia soggetto a errori, tuttavia entrambi sono limitati rispetto a ciò che l'I / O C ++ può fare. I / O C ++ (usando <<e >>) è, rispetto a C (usando printf()e scanf()):

  • Più sicuro per i tipi: con <iostream>, il tipo di oggetto che è I / O'd è noto staticamente dal compilatore. Al contrario, <cstdio>utilizza i campi "%" per capire i tipi in modo dinamico.
  • Meno soggetto a errori: con <iostream>, non ci sono token "%" ridondanti che devono essere coerenti con gli oggetti reali che sono I / O. La rimozione della ridondanza rimuove una classe di errori.
  • Estensibile: il <iostream>meccanismo C ++ consente I / O di nuovi tipi definiti dall'utente senza rompere il codice esistente. Immagina il caos se tutti aggiungessero simultaneamente nuovi campi "%" incompatibili a printf()e scanf()?!
  • Eredita: il <iostream>meccanismo C ++ è costruito da classi reali come std::ostreame std::istream. A differenza <cstdio>di quelli FILE*, si tratta di classi reali e quindi ereditabili. Ciò significa che puoi avere altre cose definite dall'utente che sembrano e si comportano come flussi, ma che fanno qualsiasi cosa strana e meravigliosa tu voglia. Puoi usare automaticamente i miliardi di righe di codice I / O scritti da utenti che non conosci nemmeno e che non hanno bisogno di conoscere la tua classe "stream esteso".

D'altra parte, printfè significativamente più veloce, il che può giustificare l'utilizzo preferibilmente coutin casi molto specifici e limitati. Profili sempre per primo. (Vedi, ad esempio, http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /)


2
D'altra parte, c'è la libreria FastFormat ( fastformat.org ), che offre sicurezza dei tipi, espressività e prestazioni contemporaneamente. (Non che ci abbia ancora provato ...)
xtofl

3
@Marcelo probabilmente perché è un buon riassunto, con tutto citato. La formattazione ... sì, è piuttosto male. Avrei dovuto risolverlo da solo, ma sembra che altri (incluso te stesso) se ne siano occupati, il che, ovviamente, è più costruttivo del semplice piagnisteo.
Mikeage,

2
Di recente printf()dovrebbe anche essere estensibile. Vedi "ganci printf" su udrepper.livejournal.com/20948.html
Maxim

4
@MaximYegorushkin: lo standard printfnon ha tale capacità. I meccanismi di libreria non portatili non sono quasi allo stesso livello dell'estensibilità completamente standardizzata degli iostreams.
Ben Voigt,

4
"D'altra parte, printf è significativamente più veloce" printf è anche più pulito e più facile da usare, motivo per cui evito cout quando possibile.
FluorescentGreen5,

43

Le persone spesso affermano che printfè molto più veloce. Questo è in gran parte un mito. L'ho appena testato, con i seguenti risultati:

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

Conclusione: se si desidera solo nuove righe, utilizzare printf; altrimenti, coutè quasi altrettanto veloce o persino più veloce. Maggiori dettagli possono essere trovati sul mio blog .

Per essere chiari, non sto cercando di dire che iostreamsono sempre meglio di printf; Sto solo cercando di dire che dovresti prendere una decisione informata sulla base di dati reali, non un'ipotesi selvaggia basata su un presupposto comune e fuorviante.

Aggiornamento: ecco il codice completo che ho usato per i test. Compilato con g++senza ulteriori opzioni (a parte -lrtper i tempi).

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

5
Nei tuoi punteggi, i battiti di stampa possono essere facilmente calcolati (maggior parte dei casi). Mi chiedo perché mi consiglia di utilizzare Cout quando si tratta di perf. Anche se concordo che perf non è troppo diverso in casi realistici ..
mishal153

3
@ mishal153: Sto solo cercando di dire che la performance non è troppo diversa, quindi il consiglio comunemente ascoltato di "non usare mai cout perché è lentamente lento" è semplicemente stupido. Nota che cout ha l'ovvio vantaggio della sicurezza del tipo e spesso anche della leggibilità. (La formattazione in virgola mobile con iostreams è orribile ...)
Thomas,

35
La differenza importante tra printf()e std::ostreamè che il primo genera tutti gli argomenti in una singola chiamata mentre std::ostreamincorre in una chiamata separata per ciascuno <<. Il test genera solo un argomento e una nuova riga, ecco perché non puoi vedere la differenza.
Maxim Egorushkin,

12
Il compilatore dovrebbe essere in grado di incorporare queste chiamate. Inoltre, printfpotrebbe fare molte chiamate sotto le copertine per le funzioni di supporto per vari identificatori di formattazione ... che, o è una mostruosa funzione monolitica. E ancora, a causa dell'inline, non dovrebbe fare alcuna differenza in termini di velocità.
Thomas,

4
Hai cronometrato il tuo terminale. Utilizzare sprintfo fprintfe stringstreamo fstream.
Ben Voigt,

41

E cito :

In termini di alto livello, le principali differenze sono la sicurezza dei tipi (cstdio non ce l'ha), le prestazioni (la maggior parte delle implementazioni di iostreams sono più lente di quelle cstdio) e l'estensibilità (iostreams consente target di output personalizzati e output senza interruzioni di tipi definiti dall'utente).


Soprattutto su unix in cui con POSIX non sai mai quale sia la dimensione di uno dei typedef, quindi hai bisogno di un sacco di cast o dato che il 99% dei programmi lo rischi con% d. Ha impiegato molto tempo prima che% z arrivasse con C99. Ma per time_t / off_t la ricerca dell'istruzione di formato corretta continua.
Lothar,

30

Una è una funzione che stampa su stdout. L'altro è un oggetto che fornisce diverse funzioni membro e sovraccarichi di operator<<quella stampa su stdout. Ci sono molte altre differenze che potrei enumerare, ma non sono sicuro di cosa stai cercando.


12

Per me, le vere differenze che mi farebbero andare per "cout" piuttosto che "printf" sono:

1) << L' operatore può essere sovraccarico per le mie lezioni.

2) Il flusso di output per cout può essere facilmente modificato in un file: (: copia incolla :)

#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) Trovo che cout sia più leggibile, specialmente quando abbiamo molti parametri.

Un problema con coutè le opzioni di formattazione. La formattazione dei dati (precisione, giustificazione, ecc.) printfÈ più semplice.


1
è carino. Come posso sapere che nessuno modifica il cout globale in questo modo in qualche thread di libreria straniera?
vp_arth,

1
Puoi facilmente passare printfa un file anche sostituendolo con fprintf...
CoffeeTableEspresso

5

Due punti non menzionati altrimenti qui che trovo significativi:

1) couttrasporta molti bagagli se non si utilizza già la STL. Aggiunge oltre il doppio del codice al file oggetto di printf. Questo vale anche per string, e questa è la ragione principale per cui tendo a usare la mia libreria di stringhe.

2) coututilizza <<operatori sovraccarichi , che trovo sfortunato. Questo può aggiungere confusione se stai usando anche il<< operatore per lo scopo previsto (spostamento a sinistra). Personalmente non mi piace sovraccaricare gli operatori per scopi tangenziali per l'uso previsto.

In conclusione: userò cout (e string) se sto già usando l'STL. Altrimenti, tendo ad evitarlo.


4

Con i primitivi, probabilmente non importa del tutto quale usi. Dico dove diventa utile quando si desidera produrre oggetti complessi.

Ad esempio, se hai una lezione,

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

Ora, quanto sopra potrebbe non sembrare eccezionale, ma supponiamo che sia necessario emetterlo in più punti del codice. Non solo, diciamo che aggiungi un campo "int d". Con cout, devi cambiarlo solo una volta. Tuttavia, con printf, dovresti cambiarlo in molti posti e non solo, devi ricordare a te stesso quali produrre.

Detto questo, con cout, puoi ridurre un sacco di tempo speso con la manutenzione del tuo codice e non solo se riutilizzi l'oggetto "Something" in una nuova applicazione, non devi preoccuparti dell'output.


Inoltre, per aggiungere qualcosa in merito alle prestazioni, direi che non dovresti produrre nulla se l'applicazione è fatta per le prestazioni. Qualsiasi tipo di output su std è piuttosto costoso e lento. Dico che dovresti evitarlo e produrlo solo quando è assolutamente necessario farlo.
Daniel,

tieni presente che la tua classe potrebbe avere membri privati ​​a cui non puoi accedere così facilmente dall'esterno. Con l'operatore di output, hai esattamente una posizione che deve essere amica della tua classe e ora puoi emetterla ovunque, anche nel codice che non conoscevi.
Hochl,

2

Ovviamente puoi scrivere "qualcosa" un po 'meglio per mantenere la manutenzione:

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

E un test un po 'esteso di cout vs. printf, ha aggiunto un test di' double ', se qualcuno vuole fare altri test (Visual Studio 2008, versione di rilascio dell'eseguibile):

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

Il risultato è:

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

Wow, perché è endlmolto meno efficiente di '\n'?
Nicholas Hamilton,

1
Credo che sia perché endlscarica il buffer e \nnon lo è, anche se non sono sicuro che questo sia definitivamente il motivo.
Caleb Xu,

Questa non è una risposta alla domanda, è più come una risposta a quella di Daniel e Thomas .
Fabio dice di reintegrare Monica l'

2

Vorrei sottolineare che se vuoi giocare con i thread in C ++, se lo usi coutpuoi ottenere risultati interessanti.

Considera questo codice:

#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

Ora, l'output viene tutto mischiato. Può produrre anche risultati diversi, prova a eseguire più volte:

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

Puoi usare printfper farlo bene, oppure puoi usare mutex.

#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB
#1: AAA
#2: BBB

Divertiti!


2
wtf threadnon fa impazzire l'output. Ho appena riprodotto e trovato entrambi xyze ABCnell'output. Non c'è stato un assalto b / n ABCcome ABABAB.
Abhinav Gauniyal,

1
Non so come coutfunziona con i thread, ma so per certo che il codice che stai mostrando non è quello che hai usato per ottenere questi output. Il codice passa la stringa "ABC"per il thread 1 e "xyz"per il thread 2, ma l'output mostra AAAe BBB. Correggi, perché in questo momento è confuso.
Fabio dice di reintegrare Monica l'

1
cout<< "Hello";
printf("%s", "Hello"); 

Entrambi sono usati per stampare valori. Hanno una sintassi completamente diversa. C ++ ha entrambi, C ha solo printf.


19
... che cosa? hai confuso qualcosa?
xtofl,

1
Risolto il problema -1 perché ha richiesto una correzione e la risposta lascia molto a desiderare.
Yacoby,

3
I nomi delle funzioni erano stati invertiti: cout era usato con la sintassi di printf e printf era usato con la sintassi di cout. Non avrei dovuto nemmeno essere accettato!
Mahmoud Al-Qudsi,

2
e il principale svantaggio di cout è che usa l'operatore << che è verboso e brutto e probabilmente l'abuso dell'operatore. :)
jalf

8
Sebbene questa non sia la risposta migliore, non capisco come scatman sia punito per la sua risposta solo perché è stata scelta come la migliore risposta. xbit ha una risposta peggiore IMO ma ha -1 voto. Non sto dicendo che xbit debba essere votato in ribasso, ma non vedo che sia giusto votare scatman per l'errore dell'OP più di quanto non debba essere ...
Jesse,

1

Vorrei dire che la mancanza di estensibilità non printfè del tutto vera:
in C è vera. Ma in C non ci sono classi reali.
In C ++, è possibile sovraccaricare l'operatore di cast, quindi, sovraccaricando un char*operatore e usando printfcosì:

Foo bar;
...;
printf("%s",bar);

può essere possibile, se Foo sovraccarica l'operatore buono. O se hai fatto un buon metodo. In breve, printfè estensibile come coutper me.

Gli argomenti tecnici che posso vedere per i flussi C ++ (in generale ... non solo cout.) Sono:

  • Sicurezza rispetto ai tipi. (E, a proposito, se voglio stampare un singolo '\n'che usoputchar('\n') ... Non userò una bomba atomica per uccidere un insetto.).

  • Più semplice da imparare. (nessun parametro "complicato" da imparare, solo da usare <<e >>operatori)

  • Lavora nativamente con std::string(perché printfc'è std::string::c_str(), ma per scanf?)

Perché printfvedo:

  • Formattazione complessa più semplice o almeno più breve (in termini di caratteri scritti). Molto più leggibile, per me (questione di gusti immagino).

  • Migliore controllo di ciò che la funzione ha fatto (Restituisce quanti caratteri sono stati scritti e c'è il %nformattatore: "Niente di stampato. L'argomento deve essere un puntatore a un int firmato, dove è memorizzato il numero di caratteri scritti finora." (Da printf - Riferimento C ++ )

  • Migliori possibilità di debug. Per lo stesso motivo dell'ultimo argomento.

Le mie preferenze personali vanno a printf(e scanf) funzioni, principalmente perché amo le righe brevi e perché non penso che i problemi di tipo sulla stampa del testo siano davvero difficili da evitare. L'unica cosa che deploro con le funzioni in stile C è che std::stringnon è supportato. Dobbiamo passare attraverso un char*prima di darlo a printf(con il std::string::c_str()se vogliamo leggere, ma come scrivere?)


3
Il compilatore non ha informazioni sul tipo per le funzioni varargs, quindi non converte il parametro effettivo (tranne le promozioni degli argomenti predefiniti , come le promozioni integrali standard). Vedi 5.2.2p7. Una conversione definita dall'utente char*non verrà utilizzata.
Ben Voigt,

Anche se funzionasse, non sarebbe un esempio di estensibilità dello sprint, solo un trucco intelligente per dare allo sprint ciò che si aspetta, e ignora alcuni seri problemi come dove le char*vite e per quanto tempo e i pericoli definiti dall'utente cast impliciti.
Marcelo Cantos,

1

Altre differenze: "printf" restituisce un valore intero (uguale al numero di caratteri stampati) e "cout" non restituisce nulla

E.

cout << "y = " << 7; non è atomico.

printf("%s = %d", "y", 7); è atomico.

cout esegue il controllo dei caratteri, printf no.

Non esiste un equivalente di iostream "% d"


3
coutnon restituisce nulla perché è un oggetto, non una funzione. operator<<restituisce qualcosa (normalmente il suo operando di sinistra, ma un valore falso in caso di errore). E in che senso la printfchiamata "atomica"?
Keith Thompson,

9
È come una bomba atomica. printf("%s\n",7);
rumore senza arti

@artlessnoise aspetta perché errore di segmentazione? %sè?
Abhinav Gauniyal,

1
Questo è il punto della dichiarazione della "bomba atomica". Un argomento printf % s deve avere un puntatore valido a una stringa terminata null. L'intervallo di memoria '7' (un puntatore) non è generalmente valido; un errore di segmentazione potrebbe essere fortunato. Su alcuni sistemi, '7' potrebbe stampare un sacco di immondizia su una console e dovresti guardarlo per un giorno prima che il programma si fermi. In altre parole, questa è una cosa negativa printf. Gli strumenti di analisi statica possono rilevare molti di questi problemi.
rumore senza arte

Mentre tecnicamente printfnon esegue il controllo dei caratteri, non ho mai usato un compilatore che non mi avvertisse degli errori di battitura con printf...
CoffeeTableEspresso

1

TL; DR: fai sempre le tue ricerche, per quanto riguarda le dimensioni del codice macchina generato , le prestazioni , la leggibilità e il tempo di codifica prima di fidarti dei commenti casuali online, incluso questo.

Non sono un esperto. Mi è appena capitato di sentire due colleghi che parlavano di come dovremmo evitare l'uso del C ++ nei sistemi embedded a causa di problemi di prestazioni. Bene, abbastanza interessante, ho fatto un benchmark basato su un vero compito di progetto.

Nel suddetto compito, abbiamo dovuto scrivere alcune configurazioni nella RAM. Qualcosa di simile a:

caffè =
zucchero caldo = nessuno
latte = petto
mac = AA: BB: CC: DD: EE: FF

Ecco i miei programmi di benchmark (Sì, so che OP ha chiesto di printf (), non di fprintf (). Cerca di catturare l'essenza e, comunque, il link di OP punta comunque a fprintf ().)

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

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

Ho fatto del mio meglio per lucidarli prima di ripercorrerli entrambi 100.000 volte. Ecco i risultati:

Programma C:

real    0m 8.01s
user    0m 2.37s
sys     0m 5.58s

Programma C ++:

real    0m 6.07s
user    0m 3.18s
sys     0m 2.84s

Dimensione del file oggetto:

C   - 2,092 bytes
C++ - 3,272 bytes

Conclusione: sulla mia piattaforma molto specifica , con un processore molto specifico , che esegue una versione molto specifica del kernel Linux , per eseguire un programma compilato con una versione molto specifica di GCC , al fine di svolgere un compito molto specifico , direi l'approccio C ++ è più adatto perché funziona in modo significativamente più veloce e offre una leggibilità molto migliore. D'altra parte, C offre un ingombro ridotto, a mio avviso, non significa quasi nulla perché la dimensione del programma non è una nostra preoccupazione.

Remeber, YMMV.


Non sono d'accordo sul fatto che C ++ sia più leggibile in questo esempio, perché l'esempio racchiude più righe in una singola chiamata printf. Questo è naturalmente meno leggibile rispetto al modo in cui hai fatto il codice C ++ e raramente viene eseguito in C perché è difficile da leggere e difficile da mantenere. Un paragone equo dividerebbe la C in stampe separate, una per la linea di copertura.
maharvey67,

1
@ maharvey67 È vero quello che hai detto. Tuttavia, l'esempio che ho fornito in C era in considerazione delle prestazioni. La chiamata pack-in-one a fprintf era già due secondi più lenta dell'equivalenza C ++. Se dovessi rendere leggibile il codice C, potrebbe essere anche più lento. Disclaimer: è stato un anno fa e ricordo di aver fatto del mio meglio per lucidare sia il codice C che C ++. Non avevo prove di chiamate separate verso fprintf sarebbe più veloce di una singola chiamata, ma il motivo per cui l'ho fatto in questo modo probabilmente indica che non lo era.
Wesley,

0

Non sono un programmatore, ma sono stato un ingegnere dei fattori umani. Penso che un linguaggio di programmazione dovrebbe essere facile da imparare, comprendere e usare, e questo richiede che abbia una struttura linguistica semplice e coerente. Sebbene tutte le lingue siano simboliche e quindi, in sostanza, arbitrarie, esistono convenzioni e seguirle rende la lingua più facile da imparare e usare.

Esistono un gran numero di funzioni in C ++ e altri linguaggi scritti come funzione (parametro), una sintassi che era originariamente utilizzata per le relazioni funzionali in matematica nell'era pre-computer. printf()segue questa sintassi e se gli autori di C ++ volessero creare un metodo logicamente diverso per leggere e scrivere file avrebbero potuto semplicemente creare una funzione diversa usando una sintassi simile.

Ovviamente in Python possiamo stampare usando anche abbastanza standard object.method sintassi , cioè variablename.print, poiché le variabili sono oggetti, ma in C ++ non lo sono.

Non mi piace la sintassi di Cout perché l'operatore << non segue alcuna regola. È un metodo o una funzione, ovvero accetta un parametro e fa qualcosa. Tuttavia è scritto come se fosse un operatore di confronto matematico. Questo è un approccio scadente dal punto di vista dei fattori umani.


-1

printfè una funzione mentre coutè una variabile.


6
Ho fatto un rollback perché, sebbene la risposta stessa possa essere sbagliata, è comunque una risposta autentica. Se pensi (correttamente) che la risposta sia sbagliata, hai due opzioni: 1) aggiungi un commento o 2) aggiungi una nuova risposta (o fai entrambe le cose). Non cambiare la risposta di qualcuno in modo tale che dica qualcosa di completamente diverso da quello che era stato progettato dall'autore.
Segna il

1
printfè una funzione, ma printf()è una funzione chiamata =)
vp_arth

cout è un oggetto, non una variabile.
Lin
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.