'printf' vs. 'cout' em C ++

Respostas:

333

Estou surpreso que todos nesta pergunta afirmem que std::couté muito melhor do que printf, mesmo que a pergunta apenas peça diferenças. Agora, há uma diferença - std::couté C ++ e printfC (no entanto, você pode usá-lo em C ++, assim como quase qualquer outra coisa em C). Agora, vou ser sincero aqui; ambos printfe std::couttêm suas vantagens.

Diferenças reais

Extensibilidade

std::couté extensível. Eu sei que as pessoas dirão que printfé extensível também, mas essa extensão não é mencionada no padrão C (então você teria que usar recursos não-padrão - mas nem mesmo um recurso não-padrão comum existe), e essas extensões são uma letra (é fácil entrar em conflito com um formato já existente).

Ao contrário printf, std::coutdepende completamente da sobrecarga do operador, portanto não há problema com formatos personalizados - tudo o que você faz é definir uma sub-rotina que assuma std::ostreamo primeiro argumento e seu tipo como o segundo. Como tal, não há problemas no espaço para nome - contanto que você tenha uma classe (que não se limita a um caractere), poderá trabalharstd::ostream sobrecarga nela.

No entanto, duvido que muitas pessoas desejem estender ostream(para ser sincero, raramente vi essas extensões, mesmo que sejam fáceis de fazer). No entanto, está aqui se você precisar.

Sintaxe

Como pode ser facilmente percebido, ambos printfe std::coutusam sintaxe diferente. printfusa sintaxe de função padrão usando seqüência de caracteres padrão e listas de argumentos de comprimento variável. Na verdade, printfé uma razão pela qual C os possui - os printfformatos são complexos demais para serem utilizáveis ​​sem eles. No entanto, std::coutusa uma API diferente - a operator <<API que retorna automaticamente.

Geralmente, isso significa que a versão C será mais curta, mas na maioria dos casos não importa. A diferença é perceptível quando você imprime muitos argumentos. Se você precisar escrever algo como Error 2: File not found., assumindo o número do erro, e sua descrição for um espaço reservado, o código seria assim. Ambos os exemplos funcionam de forma idêntica (bem, std::endlna verdade , libera o buffer).

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

Embora isso não pareça muito louco (são apenas duas vezes mais), as coisas ficam mais loucas quando você formata argumentos, em vez de apenas imprimi-los. Por exemplo, imprimir algo assim 0x0424é simplesmente louco. Isso é causado pela std::coutmistura do estado e dos valores reais. Eu nunca vi uma linguagem em que algo std::setfilldesse tipo fosse um tipo (além de C ++, é claro). printfsepara claramente argumentos e tipo real. Eu realmente preferiria manter a printfversão dele (mesmo que pareça meio enigmática) em comparação com a iostreamversão dele (pois contém muito ruído).

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

Tradução

É aqui que a verdadeira vantagem das printfmentiras. A printfstring de formato está bem ... uma string. Isso facilita muito a tradução, comparado ao operator <<abuso de iostream. Supondo que a gettext()função seja traduzida e você queira mostrar Error 2: File not found., o código para obter a tradução da string de formato mostrada anteriormente ficaria assim:

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

Agora, vamos supor que traduzimos para ficcional, onde o número do erro está após a descrição. A string traduzida seria semelhante %2$s oru %1$d.\n. Agora, como fazer isso em C ++? Bem, eu não tenho ideia. Eu acho que você pode falsificar iostreamquais construções printfvocê pode passar gettext, ou algo assim, para fins de tradução. Claro,$ não é padrão C, mas é tão comum que é seguro usá-lo na minha opinião.

Não é necessário lembrar / consultar a sintaxe do tipo inteiro específico

C tem muitos tipos de números inteiros, e o C ++ também. std::coutlida com todos os tipos para você, enquanto printfrequer uma sintaxe específica, dependendo de um tipo inteiro (existem tipos não inteiros, mas o único tipo não inteiro com o qual você utilizará na prática printfé is const char *(cadeia C, pode ser obtida usando o to_cmétodo de std::string)). Por exemplo, para imprimir size_t, você precisa usar %zd, enquanto int64_texigirá o uso %"PRId64". As tabelas estão disponíveis em http://en.cppreference.com/w/cpp/io/c/fprintf e http://en.cppreference.com/w/cpp/types/integer .

Você não pode imprimir o byte NUL, \0

Como printfusa seqüências de caracteres C em vez de seqüências de caracteres C ++, ele não pode imprimir bytes NUL sem truques específicos. Em certos casos, é possível usar %ccom'\0' como um argumento, apesar de que é claramente um hack.

Diferenças com as quais ninguém se importa

atuação

Atualização: Acontece que iostreamé tão lento que geralmente é mais lento que o seu disco rígido (se você redirecionar o programa para o arquivo). Desabilitar a sincronização com stdiopode ajudar, se você precisar gerar muitos dados. Se o desempenho for uma preocupação real (em vez de escrever várias linhas no STDOUT), basta usar printf.

Todo mundo pensa que se importa com o desempenho, mas ninguém se incomoda em mensurá-lo. Minha resposta é que a E / S é um gargalo de qualquer maneira, não importa se você usa printfou iostream. Eu acho que printf poderia ser mais rápido desde uma rápida olhada no assembly (compilado com clang usando a -O3opção do compilador). Assumindo o meu exemplo de erro, o printfexemplo faz muito menos chamadas que o coutexemplo. Isto é int maincom 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:

Você pode perceber facilmente que duas seqüências de caracteres e 2(número) são enviadas como printfargumentos. É sobre isso; Não há mais nada. Para comparação, isso é iostreamcompilado para montagem. Não, não há inlining; toda operator <<chamada significa outra chamada com outro conjunto de argumentos.

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:

No entanto, para ser honesto, isso não significa nada, pois a E / S é o gargalo de qualquer maneira. Eu só queria mostrar queiostream não é mais rápido porque é "tipo seguro". A maioria das implementações de C implementa printfformatos usando goto computado, de modo que printfé o mais rápido possível, mesmo sem o compilador estar ciente printf(não que eles não estejam - alguns compiladores podem otimizar printfem certos casos) - as constantes que terminam com \ngeralmente são otimizadas paraputs ) .

Herança

Não sei por que você gostaria de herdar ostream, mas não me importo. Também é possível com FILE.

class MyFile : public FILE {}

Digite segurança

É verdade que as listas de argumentos de comprimento variável não têm segurança, mas isso não importa, pois os compiladores C populares podem detectar problemas com a printfsequência de formato se você ativar avisos. De fato, o Clang pode fazer isso sem ativar avisos.

$ 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);
     ^
Konrad Borowski
fonte
18
Você diz que a E / S é o gargalo de qualquer maneira. Obviamente você nunca testou essa suposição. Por outro lado, cito: "Por outro lado, a versão iostreams, a 75,3 MB / s, não pode armazenar dados em buffer com rapidez suficiente para acompanhar um disco rígido. Isso é ruim e ainda nem está fazendo nenhum trabalho real. Eu não acho que tenho expectativas muito altas quando digo que minha biblioteca de E / S deve ser capaz de saturar meu controlador de disco ".
Ben Voigt 15/01
4
@ BenVoigt: Admito, tento evitar C ++ sempre que possível. Eu tentei usá-lo muito, mas era mais irritante e menos sustentável do que a outra linguagem de programação que eu usava. Esse é outro motivo para eu evitar o C ++ - isso nem é rápido (nem é iostream - toda a biblioteca C ++ é lenta na maioria das implementações, talvez com exceção de std::sort, que é surpreendentemente rápido em comparação com qsort(2 vezes), em custo do tamanho do executável).
precisa saber é o seguinte
3
Ninguém aqui mencionou problemas em ambiente paralelo ao usar o cout.
Nicholas Hamilton
9
Seu argumento de desempenho não faz nenhum sentido. Mais montagem no seu programa não significa que o programa será mais lento, porque você não está contabilizando todo o código que faz a função printf, que é uma grande quantidade de código. Na minha opinião, é possível otimizar cout com << operador muito melhor que o printf, porque o compilador pode entender melhor as variáveis ​​e a formatação.
precisa saber é o seguinte
18
Gosto de muitas coisas sobre essa resposta, mas talvez minha parte favorita seja "Todo mundo pensa que se importa com o desempenho, mas ninguém se incomoda em mensurá-lo".
Kyle Strand
203

No FAQ do C ++ :

[15.1] Por que devo usar em <iostream> vez do tradicional <cstdio>?

Aumente a segurança de tipos, reduza erros, permita extensibilidade e forneça herdabilidade.

printf()indiscutivelmente não está quebrado e scanf()talvez seja habitável, apesar de propenso a erros, no entanto, ambos são limitados com relação ao que a C / E / S pode fazer. A E / S C ++ (usando <<e >>) é relativa a C (usando printf()e scanf()):

  • Mais seguro para <iostream>o tipo : Com , o tipo de objeto que está sendo E / S é conhecido estaticamente pelo compilador. Por outro lado, <cstdio>usa os campos "%" para descobrir os tipos dinamicamente.
  • Menos propenso a erros: com <iostream>, não há tokens "%" redundantes que precisam ser consistentes com os objetos reais com E / S. A remoção de redundância remove uma classe de erros.
  • Extensível: O <iostream>mecanismo C ++ permite que novos tipos definidos pelo usuário sejam E / S sem quebrar o código existente. Imagine o caos se todos estivessem simultaneamente adicionando novos campos "%" incompatíveis a printf()e scanf()?!
  • Herdável: O <iostream>mecanismo C ++ é criado a partir de classes reais como std::ostreame std::istream. Ao contrário <cstdio>do que são FILE*, são classes reais e, portanto, herdáveis. Isso significa que você pode ter outras coisas definidas pelo usuário que parecem e agem como fluxos, mas que fazem as coisas estranhas e maravilhosas que você deseja. Você automaticamente usa os zilhões de linhas de código de E / S escritas por usuários que você nem conhece, e eles não precisam saber sobre sua classe "stream estendido".

Por outro lado, printfé significativamente mais rápido, o que pode justificar usá-lo de preferência coutem casos muito específicos e limitados. Sempre perfil primeiro. (Veja, por exemplo, http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /)

Marcelo Cantos
fonte
2
Por outro lado, existe a biblioteca FastFormat ( fastformat.org ), oferecendo segurança de tipo, expressividade e desempenho de uma só vez. (Não que eu tentei ainda ...)
xtofl
3
@ Marcelo provavelmente porque é um bom resumo, com tudo o que foi citado. A formatação ... sim, isso é muito ruim. Eu deveria ter resolvido isso sozinho, mas parece que outros (inclusive você) cuidaram disso, o que, é claro, é mais construtivo do que apenas lamentar.
Mikeage
2
Ultimamente, printf()também é suposto ser extensível. Veja "printf hooks" em udrepper.livejournal.com/20948.html
Maxim Egorushkin
4
@MaximYegorushkin: Standard printfnão tem essa capacidade. Mecanismos de biblioteca não portáteis dificilmente estão no mesmo nível da extensibilidade totalmente padronizada dos iostreams.
Ben Voigt
4
"Por outro lado, printf é significativamente mais rápido" printf também é mais limpo e fácil de usar, e é por isso que evito cout quando possível.
FluorescentGreen5
43

As pessoas costumam afirmar que printfé muito mais rápido. Isso é em grande parte um mito. Acabei de o testar, com os seguintes resultados:

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

Conclusão: se você deseja apenas novas linhas, use printf; caso contrário, couté quase tão rápido ou até mais rápido. Mais detalhes podem ser encontrados no meu blog .

Para deixar claro, não estou tentando dizer que iostreams são sempre melhores que printf; Estou apenas tentando dizer que você deve tomar uma decisão informada com base em dados reais, não um palpite selvagem, com base em alguma suposição comum e enganosa.

Atualização: Aqui está o código completo que usei para testar. Compilado g++sem opções adicionais (além do -lrttempo).

#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);
    }
}
Thomas
fonte
5
Na sua pontuação, o printf beats cout facilmente (maioria dos casos). Gostaria de saber por que você recomenda usar cout quando se trata de perf. Embora eu concorde perf não é muito diferente em casos realista ..
mishal153
3
@ mishal153: Estou apenas tentando dizer que o desempenho não é muito diferente, então o conselho comum de "nunca usar cout porque está muito lento" é completamente estúpido. Observe que o cout tem a vantagem óbvia de segurança de tipo e, frequentemente, legibilidade também. (Formatação com iostreams de ponto flutuante é horrível ...)
Thomas
35
A diferença importante entre printf()e std::ostreamé que o primeiro gera todos os argumentos em uma única chamada, ao passo que std::ostreamgera uma chamada separada para cada um <<. O teste gera apenas um argumento e uma nova linha, é por isso que você não vê a diferença.
Maxim Egorushkin
12
O compilador deve ser capaz de incorporar essas chamadas. Além disso, printfpode fazer muitas chamadas sob as cobertas para funções auxiliares para vários especificadores de formatação ... isso, ou é uma função monolítica monstruosa. E, novamente, por causa do inline, não deve fazer diferença na velocidade.
26411 Thomas
4
Você cronometrou seu terminal. Use sprintfou fprintfe stringstreamou fstream.
Ben Voigt 15/01
41

E cito :

Em termos de alto nível, as principais diferenças são segurança de tipo (o cstdio não o possui), desempenho (a maioria das implementações do iostreams são mais lentas que as do cstdio) e extensibilidade (o iostreams permite destinos de saída personalizados e saída contínua de tipos definidos pelo usuário).

Kyle Rosendo
fonte
Especialmente no unix, onde, com o POSIX, você nunca sabe qual é o tamanho de um dos typedefs, portanto, você precisa de muitas transmissões ou, como 99% dos programas, apenas o arrisca com% d. Demorou muito tempo até o% z chegar com o C99. Mas, para time_t / off_t, a busca pela instrução correta de formato continua.
Lothar
30

Uma é uma função que imprime no stdout. O outro é um objeto que fornece várias funções de membro e sobrecargas operator<<dessa impressão para stdout. Eu poderia enumerar muitas outras diferenças, mas não tenho certeza do que você está procurando.

Marcelo Cantos
fonte
12

Para mim, as diferenças reais que me levariam a cout em vez de 'printf' são:

1) << operador pode ser sobrecarregado para minhas classes.

2) O fluxo de saída do cout pode ser facilmente alterado para um arquivo: (: copy paste :)

#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) Acho que o cout é mais legível, principalmente quando temos muitos parâmetros.

Um problemacout é com as opções de formatação. Formatar os dados (precisão, justificativa etc.) printfé mais fácil.

mishal153
fonte
11
é legal. Como posso saber que ninguém modifica o cout global dessa maneira em algum thread de biblioteca estrangeira?
Vp_arth
11
Você pode facilmente mudar printfpara um arquivo, substituindo-o por fprintf...
CoffeeTableEspresso
5

Dois pontos não mencionados aqui que considero significativos:

1) coutcarrega muita bagagem se você ainda não estiver usando o STL. Ele adiciona mais de duas vezes mais código ao seu arquivo de objeto que printf. Isso também é verdade string, e esse é o principal motivo pelo qual eu uso minha própria biblioteca de strings.

2) coutusa <<operadores sobrecarregados , o que acho infeliz. Isso pode causar confusão se você também estiver usando o <<operador para a finalidade a que se destina (pressione a esquerda). Pessoalmente, não gosto de sobrecarregar os operadores para fins tangenciais ao uso pretendido.

Conclusão: usarei cout(e string) se já estiver usando o STL. Caso contrário, eu tendem a evitá-lo.

Bill Weinman
fonte
4

Com os primitivos, provavelmente não importa totalmente qual deles você usa. Eu digo que é útil quando você deseja gerar objetos complexos.

Por exemplo, se você tem uma aula,

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

Agora, o que foi dito acima pode não parecer tão bom, mas vamos supor que você precise produzi-lo em vários locais do seu código. Além disso, digamos que você adicione um campo "int d". Com cout, você só precisa trocá-lo uma vez. No entanto, com o printf, você teria que alterá-lo em vários lugares, e não apenas isso, você deve se lembrar quais devem ser impressos.

Dito isso, com o cout, é possível reduzir muito tempo gasto com a manutenção do seu código e não apenas se você reutilizar o objeto "Algo" em um novo aplicativo, não precisa se preocupar com a saída.

Daniel
fonte
Além disso, para adicionar a questão do desempenho, eu diria que você não deve produzir nada se seu aplicativo for feito para desempenho. Qualquer tipo de saída para std é bastante caro e lento. Eu digo que você deve evitá-lo e produzir apenas quando for absolutamente necessário fazê-lo.
Daniel
lembre-se de que sua turma pode ter membros particulares que você não pode acessar tão facilmente de fora. Com o operador de saída, você tem exatamente um local que precisa ser amigo da sua classe e agora pode produzi-lo em qualquer lugar, mesmo no código que você não conhecia.
Hochl 04/08/19
2

Claro que você pode escrever "algo" um pouco melhor para manter a manutenção:

#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 um teste um pouco estendido de cout vs. printf, adicionou um teste de 'double', se alguém quiser fazer mais testes (Visual Studio 2008, versão de lançamento do executável):

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

O resultado é:

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
LuP
fonte
Uau, por que é endltão menos eficiente que '\n'?
Nicholas Hamilton
11
Acredito que é porque endllibera o buffer, e \nnão, embora não tenha certeza de que esse seja definitivamente o motivo.
Caleb Xu
Isso não é uma resposta para a pergunta, é mais como uma resposta para Daniel e Thomas .
Fabio diz Reinstate Monica em
2

Gostaria de salientar que, se você quiser brincar com tópicos em C ++, se você usar, coutpoderá obter alguns resultados interessantes.

Considere este código:

#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

Agora, a saída é toda embaralhada. Também pode gerar resultados diferentes, tente executar várias vezes:

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

Você pode usar printfpara acertar ou usar mutex.

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

Diverta-se!

Apollo
fonte
2
wtf threads não faz a saída ficar louca. Acabei de reproduzir e encontrei ambos xyze ABCna saída. Não houve mutilação b / w ABCas ABABAB.
Abhinav Gauniyal
11
Não sei como coutfunciona com threads, mas tenho certeza de que o código que você está mostrando não é o que você usou para obter essas saídas. Seu código passa a string "ABC"para o thread 1 e "xyz"para o thread 2, mas sua saída mostra AAAe BBB. Por favor, corrija, porque agora é confuso.
Fabio diz Reinstate Monica em
1
cout<< "Hello";
printf("%s", "Hello"); 

Ambos são usados ​​para imprimir valores. Eles têm uma sintaxe completamente diferente. C ++ possui ambos, C apenas possui printf.

scatman
fonte
19
... o que? você misturou alguma coisa?
Xtofl 20/05/10
11
Corrigido o problema. -1 porque exigia correção e a resposta deixa muito a desejar.
Yacoby
3
Os nomes das funções foram revertidos: cout foi usado com a sintaxe printf e printf foi usado com a sintaxe cout. Não deveria ter sido aceito!
Mahmoud Al-Qudsi
2
e a principal desvantagem do cout é que ele usa o operador <<, que é detalhado e feio e, sem dúvida, o abuso do operador. :)
jalf
8
Embora essa não seja a melhor resposta, não entendo como o scatman está sendo punido por sua resposta apenas porque foi escolhido como a melhor resposta. O xbit tem uma resposta muito pior à IMO, mas tem -1 de voto. Eu não estou dizendo XBIT deve ser para baixo votado mais, mas eu não vê-lo sendo justo para baixo scatman voto para o erro do OP mais do que tem que ser ...
Jesse
1

Eu gostaria de dizer que a falta de extensibilidade não printfé inteiramente verdadeira:
em C, é verdade. Mas em C, não há classes reais.
Em C ++, é possível sobrecarregar o operador de conversão, sobrecarregando um char*operador e usando printfassim:

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

possível, se Foo sobrecarregar o bom operador. Ou se você fez um bom método. Em suma, printfé tão extensível quanto coutpara mim.

Argumento técnico que posso ver para fluxos C ++ (em geral ... não apenas cout.) São:

  • Segurança tipográfica. (E, a propósito, se eu quiser imprimir uma única que '\n'eu uso putchar('\n')... não usarei uma bomba nuclear para matar um inseto.).

  • Mais simples de aprender. (sem parâmetros "complicados" para aprender, apenas para usar <<e >>operadores)

  • Trabalhar nativamente com std::string(pois printfexiste std::string::c_str(), mas para scanf?)

Pois printfeu vejo:

  • Formatação complexa mais fácil, ou pelo menos mais curta (em termos de caracteres escritos). Muito mais legível para mim (questão de gosto, eu acho).

  • Melhor controle do que a função fez (Retorne quantos caracteres foram escritos e existe o %nformatador: "Nada impresso. O argumento deve ser um ponteiro para um int assinado, onde o número de caracteres gravados até agora é armazenado." ( From printf - Referência C ++ )

  • Melhores possibilidades de depuração. Pela mesma razão que o último argumento.

Minhas preferências pessoais vão para as funções printf(e scanf), principalmente porque eu amo linhas curtas e porque não acho que problemas de digitação na impressão de texto sejam realmente difíceis de evitar. A única coisa que eu deploro com funções no estilo C é que isso std::stringnão é suportado. Temos que passar por um char*antes de dar printf(com o std::string::c_str()se queremos ler, mas como escrever?)

bmorel
fonte
3
O compilador não possui informações de tipo para funções varargs, portanto, não converterá o parâmetro real (exceto promoções de argumentos padrão , como promoções integrais padrão). Veja 5.2.2p7. Uma conversão definida pelo usuário para char*não será usada.
Ben Voigt
Mesmo se isso funcionasse, não seria um exemplo de extensibilidade do sprintf, apenas um truque inteligente para dar ao sprintf o que ele espera, e ignora alguns problemas sérios, como onde as char*vidas e por quanto tempo e os perigos da definição do usuário. elencos implícitos.
Marcelo Cantos
1

Mais diferenças: "printf" retorna um valor inteiro (igual ao número de caracteres impressos) e "cout" não retorna nada

E.

cout << "y = " << 7; não é atômico.

printf("%s = %d", "y", 7); é atômico.

cout executa digitação, printf não.

Não há iostream equivalente a "% d"

skan
fonte
3
coutnão retorna nada porque é um objeto, não uma função. operator<<retorna algo (normalmente seu operando esquerdo, mas um valor falso se houver um erro). E em que sentido é o printfchamado "atômico"?
Keith Thompson
9
É como uma bomba atômica. printf("%s\n",7);
ruído artless 14/03
@artlessnoise wait por que falha na segmentação? %sé ?
Abhinav Gauniyal
11
Esse é o ponto da declaração da 'bomba atômica'. UMAprintf argumento % s deve ter um ponteiro válido para uma sequência terminada nula. O intervalo de memória '7' (um ponteiro) geralmente não é válido; uma falha de segmentação pode ter sorte. Em alguns sistemas, o '7' pode imprimir muito lixo em um console e você precisaria analisá-lo por um dia antes que o programa parasse. Em outras palavras, isso é uma coisa ruim printf. As ferramentas de análise estática podem capturar muitos desses problemas.
ruído artless
Embora tecnicamente printfnão faz typechecking, eu nunca tenha usado um compilador que não me alertar sobre erros de tipo com printf...
CoffeeTableEspresso
1

TL; DR: Sempre faça sua própria pesquisa em relação ao tamanho do código de máquina gerado , desempenho , legibilidade e tempo de codificação antes de confiar em comentários aleatórios on-line, incluindo este.

Eu não sou especialista. Acabei de ouvir dois colegas de trabalho falando sobre como devemos evitar o uso de C ++ em sistemas embarcados devido a problemas de desempenho. Bem, interessante o suficiente, fiz um benchmark com base em uma tarefa real do projeto.

Nessa tarefa, tivemos que escrever algumas configurações na RAM. Algo como:

café =
açúcar quente = nenhum
leite = peito
mac = AA: BB: CC: DD: EE: FF

Aqui estão meus programas de benchmark (Sim, eu sei que o OP perguntou sobre printf (), não fprintf (). Tente capturar a essência e, a propósito, o link do OP aponta para fprintf () de qualquer maneira.)

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

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

Eu fiz o meu melhor para polir antes de repetir as duas vezes 100.000. Aqui estão os resultados:

Programa C:

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

Programa C ++:

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

Tamanho do arquivo de objeto:

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

Conclusão: Na minha plataforma muito específica , com um processador muito específico , executando uma versão muito específica do kernel do Linux , para executar um programa que é compilado com uma versão muito específica do GCC , para realizar uma tarefa muito específica , eu diria a abordagem C ++ é mais adequada porque é executada significativamente mais rápido e oferece uma legibilidade muito melhor. Por outro lado, C oferece pouca presença, na minha opinião, significa quase nada porque o tamanho do programa não é da nossa preocupação.

Lembre-se, YMMV.

Wesley
fonte
Não concordo que C ++ seja mais legível neste exemplo, porque seu exemplo agrupa várias linhas em uma única chamada printf. Isso é naturalmente menos legível do que o código C ++ e raramente é feito em C porque é difícil de ler e de manter. Uma comparação justa espalharia o C em impressões separadas, uma para a linha de alcance.
precisa saber é o seguinte
11
@ maharvey67 É verdade o que você disse. No entanto, o exemplo que forneci em C foi em consideração ao desempenho. A chamada empacotada em um para fprintf foi dois segundos mais lenta que a equivalência C ++ já. Se eu tornar o código C legível, pode ser ainda mais lento. Isenção de responsabilidade: isso foi há um ano e lembro-me de que tentei o meu melhor para polir o código C e C ++. Eu não tinha provas de que chamadas separadas para fprintf seriam mais rápidas que uma única chamada, mas o motivo pelo qual fiz dessa maneira provavelmente indica que não era.
Wesley19 /
0

Não sou programador, mas sou engenheiro de fatores humanos. Eu sinto que uma linguagem de programação deve ser fácil de aprender, entender e usar, e isso exige que ela tenha uma estrutura lingüística simples e consistente. Embora todas as línguas sejam simbólicas e, portanto, em sua essência, arbitrárias, existem convenções e segui-las facilita o aprendizado e o uso da língua.

Há um grande número de funções em C ++ e outras linguagens escritas como função (parâmetro), uma sintaxe que foi originalmente usada para relacionamentos funcionais em matemática na era anterior ao computador. printf()segue esta sintaxe e se os escritores do C ++ desejassem criar qualquer método logicamente diferente para ler e gravar arquivos, eles poderiam simplesmente criar uma função diferente usando uma sintaxe semelhante.

No Python, é claro que podemos imprimir usando a object.methodsintaxe também bastante padrão , ou seja, variableename.print, já que variáveis ​​são objetos, mas em C ++ elas não são.

Não gosto da sintaxe cout porque o operador << não segue nenhuma regra. É um método ou função, ou seja, pega um parâmetro e faz algo a ele. No entanto, está escrito como se fosse um operador de comparação matemática. Esta é uma péssima abordagem do ponto de vista dos fatores humanos.

Daniel Woodard
fonte
-1

printfé uma função enquanto couté uma variável.

John
fonte
6
Fiz uma reversão porque, embora a resposta em si possa estar errada, ainda é uma resposta genuína. Se você (corretamente) achar que a resposta está errada, você tem duas opções: 1) adicione um comentário ou 2) adicione uma nova resposta (ou faça as duas coisas). Não mude a resposta de alguém para que ela diga algo completamente diferente do que o autor pretendia.
Mark
11
printfé uma função, mas printf()é uma chamada de função =)
vp_arth
cout é um objeto, não uma variável.
Lin