'printf' vs. 'cout' en C ++


Respuestas:


333

Me sorprende que todos en esta pregunta afirmen que std::coutes mucho mejor que eso printf, incluso si la pregunta solo pide diferencias. Ahora, hay una diferencia: std::coutes C ++ y printfes C (sin embargo, puede usarlo en C ++, como casi cualquier otra cosa de C). Ahora, seré honesto aquí; ambos printfy std::couttienen sus ventajas.

Diferencias reales

Extensibilidad

std::coutEs extensible. Sé que la gente dirá que también printfes extensible, pero dicha extensión no se menciona en el estándar C (por lo que tendría que usar características no estándar, pero ni siquiera existe una característica no estándar común), y tales extensiones son una letra (por lo que es fácil entrar en conflicto con un formato ya existente).

A diferencia printf, std::coutdepende completamente de la sobrecarga del operador, por lo que no hay problemas con los formatos personalizados: todo lo que debe hacer es definir una subrutina tomando std::ostreamcomo primer argumento y su tipo como segundo. Como tal, no hay problemas de espacio de nombres: siempre que tenga una clase (que no se limita a un carácter), puede tener una std::ostreamsobrecarga de trabajo .

Sin embargo, dudo que muchas personas quieran extender ostream(para ser honesto, rara vez vi tales extensiones, incluso si son fáciles de hacer). Sin embargo, está aquí si lo necesitas.

Sintaxis

Como se pudo observar con facilidad, tanto printfy std::coutusar sintaxis diferente. printfusa la sintaxis de funciones estándar usando cadenas de patrones y listas de argumentos de longitud variable. En realidad, printfes una razón por la cual C los tiene: los printfformatos son demasiado complejos para ser utilizables sin ellos. Sin embargo, std::coututiliza una API diferente: la operator <<API que se devuelve.

En general, eso significa que la versión C será más corta, pero en la mayoría de los casos no importará. La diferencia es notable cuando imprime muchos argumentos. Si tiene que escribir algo como Error 2: File not found., suponiendo un número de error, y su descripción es un marcador de posición, el código se vería así. Ambos ejemplos funcionan de manera idéntica (bueno, más o menos, en std::endlrealidad vacía el búfer).

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

Si bien esto no parece demasiado loco (es solo dos veces más largo), las cosas se vuelven más locas cuando realmente formatea argumentos, en lugar de simplemente imprimirlos. Por ejemplo, la impresión de algo así 0x0424es una locura. Esto es causado por la std::coutmezcla de estado y valores reales. Nunca vi un lenguaje en el que algo así std::setfillfuera un tipo (aparte de C ++, por supuesto). printfsepara claramente los argumentos y el tipo real. Realmente preferiría mantener la printfversión (incluso si parece un poco críptica) en comparación con la iostreamversión (ya que contiene demasiado ruido).

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

Traducción

Aquí es donde la verdadera ventaja de las printfmentiras. La printfcadena de formato está bien ... una cadena. Eso hace que sea realmente fácil de traducir, en comparación con el operator <<abuso de iostream. Suponiendo que la gettext()función se traduce y desea mostrar Error 2: File not found., el código para obtener la traducción de la cadena de formato mostrada anteriormente se vería así:

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

Ahora, supongamos que traducimos a Fictionish, donde el número de error está después de la descripción. La cadena traducida se vería así %2$s oru %1$d.\n. Ahora, ¿cómo hacerlo en C ++? Bueno, no tengo idea. Supongo que puedes hacer falsas iostreamconstrucciones a las printfque puedes pasar gettext, o algo así, con fines de traducción. Por supuesto, $no es el estándar C, pero es tan común que es seguro usarlo en mi opinión.

No tener que recordar / buscar sintaxis de tipo entero específico

C tiene muchos tipos enteros, y también C ++. std::coutmaneja todos los tipos por usted, mientras que printfrequiere una sintaxis específica dependiendo de un tipo entero (hay tipos no enteros, pero el único tipo no entero que usará en la práctica printfes const char *(cadena C, se puede obtener usando el to_cmétodo de std::string)). Por ejemplo, para imprimir size_t, necesita usar %zd, mientras int64_tque requerirá usar %"PRId64". Las tablas están disponibles en http://en.cppreference.com/w/cpp/io/c/fprintf y http://en.cppreference.com/w/cpp/types/integer .

No puede imprimir el byte NUL, \0

Debido a que printfusa cadenas C en lugar de cadenas C ++, no puede imprimir un byte NUL sin trucos específicos. En ciertos casos es posible utilizar %ccon '\0'como argumento, aunque eso es claramente un truco.

Diferencias que a nadie le importan

Actuación

Actualización: Resulta que iostreames tan lento que generalmente es más lento que su disco duro (si redirige su programa a un archivo). Deshabilitar la sincronización con stdiopuede ayudar, si necesita generar muchos datos. Si el rendimiento es una preocupación real (en lugar de escribir varias líneas en STDOUT), simplemente utilícelo printf.

Todos piensan que les importa el rendimiento, pero nadie se molesta en medirlo. Mi respuesta es que I / O es un cuello de botella de todos modos, no importa si usa printfo iostream. Creo que printf podría ser más rápido desde un vistazo rápido al ensamblaje (compilado con clang usando la -O3opción del compilador). Suponiendo que mi ejemplo de error, printfejemplo hace menos llamadas que el coutejemplo. Esto es 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:

Puede notar fácilmente que dos cadenas y 2(número) se insertan como printfargumentos. Eso es todo; no hay nada más. A modo de comparación, esto se iostreamcompila para ensamblar. No, no hay en línea; cada operator <<llamada individual significa otra llamada con otro 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:

Sin embargo, para ser honesto, esto no significa nada, ya que la E / S es el cuello de botella de todos modos. Solo quería mostrar que iostreamno es más rápido porque es "tipo seguro". La mayoría de las implementaciones de C implementan printfformatos que usan goto computarizado, por lo que printfes lo más rápido posible, incluso sin que el compilador lo sepa printf(no es que no lo sean, algunos compiladores pueden optimizar printfen ciertos casos, la secuencia constante que termina \ngeneralmente está optimizada para puts) .

Herencia

No sé por qué querrías heredar ostream, pero no me importa. Es posible con FILEtambién.

class MyFile : public FILE {}

Tipo de seguridad

Es cierto que las listas de argumentos de longitud variable no tienen seguridad, pero eso no importa, ya que los compiladores de C populares pueden detectar problemas con la printfcadena de formato si habilita las advertencias. De hecho, Clang puede hacer eso sin habilitar advertencias.

$ 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
Dices que I / O es el cuello de botella de todos modos. Obviamente nunca probaste esa suposición. Me cito a mí mismo: "Por otro lado, la versión de iostreams, a 75.3 MB / s, no puede almacenar los datos lo suficientemente rápido como para mantenerse al día con un disco duro. Eso es malo, y aún no está haciendo ningún trabajo real. "Creo que tengo expectativas demasiado altas cuando digo que mi biblioteca de E / S debería poder saturar mi controlador de disco".
Ben Voigt

44
@BenVoigt: Lo admito, trato de evitar C ++ cuando es posible. Intenté usarlo mucho, pero fue más molesto y menos mantenible que otro lenguaje de programación que usé. Esta es otra razón para evitar C ++: esto ni siquiera es rápido (ni siquiera es iostream; toda la biblioteca de C ++ es lenta en la mayoría de las implementaciones, tal vez con la excepción de std::sort, que de alguna manera es sorprendentemente rápida en comparación con qsort(2 veces), en costo del tamaño ejecutable).
Konrad Borowski

3
Nadie aquí ha mencionado problemas en un entorno paralelo cuando usa cout.
Nicholas Hamilton

99
Su argumento de rendimiento no tiene ningún sentido. Más de montaje en su programa no significa que el programa será más lenta, ya que está no representa el todo el código que hace que la función printf, que es una gran cantidad de código. En mi opinión, es posible optimizar cout con << operador mucho mejor que el printf, porque el compilador puede tener un mejor sentido de las variables y el formato.
Ignas2526

18
Me gusta mucho esta respuesta, pero quizás mi parte favorita es "Todos piensan que les importa el rendimiento, pero nadie se molesta en medirlo".
Kyle Strand

203

De las preguntas frecuentes de C ++ :

[15.1] ¿Por qué debería usar en <iostream> lugar de la tradicional <cstdio>?

Aumente la seguridad de los tipos, reduzca los errores, permita la extensibilidad y proporcione la heredabilidad.

printf()podría decirse que no está roto, y scanf()quizás sea habitable a pesar de ser propenso a errores, sin embargo, ambos están limitados con respecto a lo que C ++ I / O puede hacer. C ++ I / O (usando <<y >>) es, en relación con C (usando printf()y scanf()):

  • Más seguridad de tipo: con <iostream>, el compilador conoce estáticamente el tipo de objeto que está siendo E / S. Por el contrario, <cstdio>utiliza los campos "%" para descubrir los tipos dinámicamente.
  • Menos propenso a errores: con <iostream>, no hay tokens "%" redundantes que tengan que ser consistentes con los objetos reales que están siendo E / S. Eliminar la redundancia elimina una clase de errores.
  • Extensible: el <iostream>mecanismo C ++ permite que los nuevos tipos definidos por el usuario sean de E / S sin romper el código existente. ¡Imagínese el caos si todos agregaran simultáneamente nuevos campos "%" incompatibles a printf()y scanf()?
  • Heredable: el <iostream>mecanismo de C ++ se construye a partir de clases reales como std::ostreamy std::istream. A diferencia de <cstdio>las de FILE*estas, estas son clases reales y, por lo tanto, heredables. Esto significa que puede tener otras cosas definidas por el usuario que se ven y actúan como transmisiones, pero que hacen cualquier cosa extraña y maravillosa que desee. Puede utilizar automáticamente los miles de millones de líneas de código de E / S escritas por usuarios que ni siquiera conoce, y no necesitan saber acerca de su clase de "transmisión extendida".

Por otro lado, printfes significativamente más rápido, lo que puede justificar su uso con preferencia couten casos muy específicos y limitados. Siempre perfil primero. (Ver, por ejemplo, http://programming-designs.com/2009/02/c-speed-test-part-2-printf-vs-cout /)


2
Por otro lado, está la biblioteca FastFormat ( fastformat.org ), que ofrece seguridad de tipografía , expresividad y rendimiento a la vez. (No es que lo probé sin embargo ...)
xtofl

3
@Marcelo probablemente porque es un buen resumen, con todo lo citado. El formato ... sí, eso es bastante malo. Debería haberlo arreglado yo mismo, pero parece que otros (incluido usted mismo) se encargaron de eso, lo que, por supuesto, es más constructivo que simplemente lloriquear.
Mikeage

2
Últimamente printf()también se supone que es extensible. Ver "ganchos printf" en udrepper.livejournal.com/20948.html
Maxim Egorushkin

44
@MaximYegorushkin: Standard printfno tiene esa habilidad. Los mecanismos de biblioteca no portátiles apenas están al mismo nivel que la extensibilidad completamente estandarizada de iostreams.
Ben Voigt

44
"Por otro lado, printf es significativamente más rápido" printf también es más limpio y fácil de usar, razón por la cual evito el cout cuando es posible.
FluorescentGreen5

43

Las personas a menudo afirman que printfes mucho más rápido. Esto es en gran parte un mito. Lo acabo de probar con los siguientes 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

Conclusión: si solo desea nuevas líneas, use printf; de lo contrario, coutes casi tan rápido o incluso más rápido. Más detalles se pueden encontrar en mi blog .

Para ser claros, no estoy tratando de decir que iostreamsiempre son mejores que printf; Solo estoy tratando de decir que debe tomar una decisión informada basada en datos reales, no una suposición descabellada basada en una suposición común y engañosa.

Actualización: Aquí está el código completo que usé para las pruebas. Compilado con g++sin opciones adicionales (aparte de -lrtla sincronización).

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

55
En sus puntajes, printf supera fácilmente a cout (casos mayoritarios). Me pregunto por qué recomienda usar cout cuando se trata de perf. Aunque estoy de acuerdo perf no es muy diferente en los casos realistas ..
mishal153

3
@ mishal153: Solo estoy tratando de decir que el rendimiento no es muy diferente, por lo que el consejo comúnmente escuchado de "nunca usar cout porque es muuuuuuuuuy lento" es simplemente estúpido. Tenga en cuenta que cout tiene la ventaja obvia de la seguridad de tipos y, a menudo, también de legibilidad. (El formato de coma flotante con iostreams es horrible ...)
Thomas

35
La diferencia importante entre printf()y std::ostreames que el primero genera todos los argumentos en una sola llamada, mientras que std::ostreamincurre en una llamada separada para cada uno <<. La prueba solo genera un argumento y una nueva línea, es por eso que no puede ver la diferencia.
Maxim Egorushkin el

12
El compilador debería poder incorporar estas llamadas. Además, printfpodría hacer muchas llamadas debajo de las cubiertas para funciones auxiliares para varios especificadores de formato ... eso, o es una función monolítica monstruosa. Y de nuevo, debido a la alineación, no debería hacer ninguna diferencia en la velocidad.
Thomas

44
Cronometraste tu terminal. Use sprintfo fprintfy stringstreamo fstream.
Ben Voigt

41

Y cito :

En términos de alto nivel, las principales diferencias son la seguridad de tipo (cstdio no lo tiene), el rendimiento (la mayoría de las implementaciones de iostreams son más lentas que las de cstdio) y la extensibilidad (iostreams permite objetivos de salida personalizados y salida sin problemas de tipos definidos por el usuario).


Especialmente en unix donde con POSIX nunca se sabe qué tamaño tiene uno de los typedefs realmente, por lo que necesita muchos moldes o como el 99% de los programas simplemente lo arriesga con% d. Tomó incluso mucho tiempo antes de que% z viniera con C99. Pero para time_t / off_t la búsqueda de la instrucción de formato correcto continúa.
Lothar

30

Una es una función que imprime en stdout. El otro es un objeto que proporciona varias funciones miembro y sobrecargas de operator<<esa impresión a stdout. Hay muchas más diferencias que podría enumerar, pero no estoy seguro de lo que busca.


12

Para mí, las diferencias reales que me harían ir por 'cout' en lugar de 'printf' son:

1) << el operador se puede sobrecargar para mis clases.

2) La secuencia de salida para cout se puede cambiar fácilmente a un archivo: (: copiar pegar :)

#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) Encuentro cout más legible, especialmente cuando tenemos muchos parámetros.

Un problema con coutes las opciones de formato. Formatear los datos (precisión, justificación, etc.) printfes más fácil.


1
es agradable. ¿Cómo puedo saber que nadie modifique cout global de esta manera en un hilo de biblioteca extranjera?
vp_arth

1
También puede cambiar fácilmente printfa un archivo reemplazándolo con fprintf...
CoffeeTableEspresso

5

Dos puntos no mencionados aquí que encuentro significativos:

1) coutlleva mucho equipaje si aún no está utilizando el STL. Agrega más del doble de código a su archivo objeto que printf. Esto también es cierto para string, y esta es la razón principal por la que tiendo a usar mi propia biblioteca de cadenas.

2) coututiliza <<operadores sobrecargados , lo cual me parece desafortunado. Esto puede agregar confusión si también está utilizando el <<operador para el propósito previsto (desplazamiento a la izquierda). Personalmente no me gusta sobrecargar a los operadores con fines tangenciales a su uso previsto.

En pocas palabras: usaré cout(y string) si ya estoy usando el STL. De lo contrario, tiendo a evitarlo.


4

Con las primitivas, probablemente no importa del todo cuál uses. Digo que es útil cuando quieres generar objetos complejos.

Por ejemplo, si tienes una clase,

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

Ahora, lo anterior puede no parecer tan bueno, pero supongamos que tiene que generar esto en varios lugares en su código. No solo eso, supongamos que agrega un campo "int d". Con cout, solo tienes que cambiarlo una vez. Sin embargo, con printf, tendrías que cambiarlo posiblemente en muchos lugares y no solo eso, tienes que recordarte cuáles imprimir.

Dicho esto, con cout, puede reducir una gran cantidad de tiempo dedicado al mantenimiento de su código y no solo si reutiliza el objeto "Something" en una nueva aplicación, realmente no tiene que preocuparse por la salida.


Además, para agregar sobre el tema del rendimiento, diría que no debe generar nada si su aplicación está hecha para el rendimiento. Cualquier tipo de salida a estándar es bastante caro y lento. Le digo que debe evitarlo y solo generarlo cuando sea absolutamente necesario hacerlo.
Daniel

tenga en cuenta que su clase puede tener miembros privados a los que no puede acceder tan fácilmente desde el exterior. Con el operador de salida, tiene exactamente una ubicación que necesita ser amiga de su clase, y ahora puede generarla en cualquier lugar, incluso en código que no conocía.
hochl

2

Por supuesto, puede escribir "algo" un poco mejor para mantener el mantenimiento:

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

Y una prueba un poco extendida de cout vs printf, agregó una prueba de 'doble', si alguien quiere hacer más pruebas (Visual Studio 2008, versión de lanzamiento del ejecutable):

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

El resultado es:

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, ¿ endlpor qué es mucho menos eficiente que '\n'?
Nicholas Hamilton

1
Creo que es porque endlvacía el búfer y \nno lo hace, aunque no estoy seguro de que esta sea definitivamente la razón.
Caleb Xu

Esta no es una respuesta a la pregunta, es más como una respuesta a la de Daniel y Thomas .
Fabio dice reinstalar a Mónica

2

Me gustaría señalar que si quieres jugar con hilos en C ++, si lo coutusas puedes obtener algunos resultados interesantes.

Considera 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

Ahora, el resultado viene todo mezclado. También puede producir resultados diferentes, intente ejecutar varias veces:

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

##12::  ABABAB

Puede usarlo printfpara hacerlo bien, o puede usarlo mutex.

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

¡Que te diviertas!


2
wtf threads no hace que la producción se vuelva loca. Acabo de reproducir y encontré ambos xyzy ABCen la salida. No hubo destrozos b / w ABCcomo ABABAB.
Abhinav Gauniyal

1
No sé cómo coutfunciona con los subprocesos, pero estoy seguro de que el código que está mostrando no es el que utilizó para obtener esos resultados. Su código pasa la cadena "ABC"para el hilo 1 y "xyz"para el hilo 2, pero su salida muestra AAAy BBB. Por favor, corríjalo, porque en este momento es confuso.
Fabio dice reinstalar a Mónica

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

Ambos se utilizan para imprimir valores. Tienen una sintaxis completamente diferente. C ++ tiene ambos, C solo tiene printf.


19
... ¿qué? ¿confundiste algo?
xtofl

1
Solucionado el problema. -1 porque requirió reparación y la respuesta deja mucho que desear.
Yacoby

3
Los nombres de las funciones se invirtieron: cout se usó con la sintaxis printf y printf se usó con la sintaxis cout. ¡Ni siquiera debería haber sido aceptado!
Mahmoud Al-Qudsi

2
y la principal desventaja de cout es que usa el operador <<, que es detallado y feo, y podría decirse que es un abuso del operador. :)
jalf

8
Aunque esta certeza no es la mejor respuesta, no entiendo cómo se está castigando a scatman por su respuesta solo porque fue elegida como la mejor respuesta. xbit tiene una respuesta mucho peor IMO pero tiene -1 voto. No digo que xbit deba ser rechazado ya, pero no creo que sea justo rechazar a scatman por el error del OP más de lo que debe ser ...
Jesse

1

Me gustaría decir que la falta de extensibilidad no printfes del todo cierto:
en C, es cierto. Pero en C, no hay clases reales.
En C ++, es posible sobrecargar el operador de conversión, por lo tanto, sobrecargar un char*operador y usar printfasí:

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

puede ser posible, si Foo sobrecarga al buen operador. O si hiciste un buen método. En resumen, printfes tan extensible como coutpara mí.

El argumento técnico que puedo ver para las transmisiones de C ++ (en general ... no solo cout.) Son:

  • Tipo de seguridad. (Y, por cierto, si quiero imprimir una sola '\n'que uso putchar('\n')... no usaré una bomba nuclear para matar un insecto).

  • Más simple de aprender. (no hay parámetros "complicados" para aprender, solo para usar <<y >>operadores)

  • Trabajar de forma nativa con std::string(porque printfhay std::string::c_str(), pero para scanf?)

Por lo printfque veo:

  • Formateo complejo más fácil, o al menos más corto (en términos de caracteres escritos). Mucho más legible, para mí (cuestión de gustos, supongo).

  • Un mejor control de lo que la función hace (Retorno de la cantidad de caracteres en el escrito y no es el %nformateador: ". Nada impreso El argumento debe ser un puntero a un int firmado, en el que el número de caracteres escritos hasta ahora se almacena" (de printf - Referencia de C ++ )

  • Mejores posibilidades de depuración. Por la misma razón que el último argumento.

Mis preferencias personales van a printf(y scanf) funciones, principalmente porque me encantan las líneas cortas y porque no creo que los problemas de escritura en la impresión de texto sean realmente difíciles de evitar. Lo único que lamento con las funciones de estilo C es que std::stringno es compatible. Tenemos que pasar por un char*antes de dárselo printf(con el std::string::c_str()si queremos leer, pero ¿cómo escribir?)


3
El compilador no tiene información de tipo para las funciones varargs, por lo que no convertirá el parámetro real (excepto las promociones de argumento predeterminadas , como las promociones integrales estándar). Ver 5.2.2p7. char*No se utilizará una conversión definida por el usuario .
Ben Voigt

Incluso si esto funcionara, no sería un ejemplo de extensibilidad de sprintf, solo un truco inteligente para darle a sprintf lo que espera, e ignora algunos problemas serios, como dónde char*vive y por cuánto tiempo, y los peligros de la definición del usuario moldes implícitos.
Marcelo Cantos

1

Más diferencias: "printf" devuelve un valor entero (igual al número de caracteres impresos) y "cout" no devuelve nada

Y.

cout << "y = " << 7; No es atómico.

printf("%s = %d", "y", 7); es atómico

cout realiza la verificación de tipo, printf no.

No hay equivalente iostream de "% d"


3
coutno devuelve nada porque es un objeto, no una función. operator<<devuelve algo (normalmente su operando izquierdo, pero un valor falso si hay un error). ¿Y en qué sentido es la printfllamada "atómica"?
Keith Thompson

99
Es como una bomba atómica. printf("%s\n",7);
ruido sin arte

@artlessnoise espera por qué falla la segmentación? %ses ?
Abhinav Gauniyal

1
Ese es el punto de la declaración de 'bomba atómica'. Un argumento printf % s debe tener un puntero válido a una cadena terminada en nulo. El rango de memoria '7' (un puntero) generalmente no es válido; una falla de segmentación podría ser afortunada. En algunos sistemas, '7' podría imprimir mucha basura en una consola y tendría que mirarlo durante un día antes de que el programa se detenga. En otras palabras, esto es algo malo printf. Las herramientas de análisis estático pueden detectar muchos de estos problemas.
ruido sin arte

Aunque técnicamente printfno realiza la verificación de tipos, nunca he usado un compilador que no me haya advertido sobre errores de tipo con printf...
CoffeeTableEspresso

1

TL; DR: Siempre haga su propia investigación, con respecto al tamaño del código de máquina generado , el rendimiento , la legibilidad y el tiempo de codificación antes de confiar en los comentarios aleatorios en línea, incluido este.

No soy un experto Simplemente escuché a dos compañeros de trabajo hablar sobre cómo deberíamos evitar usar C ++ en sistemas embebidos debido a problemas de rendimiento. Bueno, lo suficientemente interesante, hice un punto de referencia basado en una tarea de proyecto real.

En dicha tarea, tuvimos que escribir alguna configuración en la RAM. Algo como:

café =
azúcar caliente = ninguna
leche = pechuga
mac = AA: BB: CC: DD: EE: FF

Aquí están mis programas de referencia (Sí, sé que OP preguntó por printf (), no por fprintf (). Intente capturar la esencia y, por cierto, el enlace de OP apunta a fprintf () de todos modos).

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

Hice mi mejor esfuerzo para pulirlos antes de enlazarlos 100,000 veces. Aquí están los 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

Tamaño del archivo de objeto:

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

Conclusión: En mi plataforma muy específica , con un procesador muy específico , ejecutando una versión muy específica del kernel de Linux , para ejecutar un programa que se compila con una versión muy específica de GCC , para realizar una tarea muy específica , diría El enfoque C ++ es más adecuado porque se ejecuta significativamente más rápido y proporciona una legibilidad mucho mejor. Por otro lado, C ofrece una huella pequeña, en mi opinión, no significa casi nada porque el tamaño del programa no es de nuestra preocupación.

Recuerda, YMMV.


No estoy de acuerdo con que C ++ sea más legible en este ejemplo, porque su ejemplo agrupa varias líneas en una sola llamada printf. Eso es naturalmente menos legible que la forma en que hizo el código C ++, y rara vez se hace en C porque es difícil de leer y de mantener. Una comparación equitativa distribuiría la C en impresiones separadas, una para la línea de alcance.
maharvey67

1
@ maharvey67 Es cierto lo que dijiste. Sin embargo, el ejemplo que proporcioné en C fue en consideración del rendimiento. La llamada empaquetada en uno a fprintf fue dos segundos más lenta que la equivalencia de C ++. Si tuviera que hacer legible el código C, entonces podría ser aún más lento. Descargo de responsabilidad: esto fue hace un año y recuerdo que hice todo lo posible para pulir tanto el código C como el C ++. No tenía pruebas de que las llamadas separadas a fprintf fueran más rápidas que una sola llamada, pero la razón por la que lo hice de esta manera probablemente indica que no fue así.
Wesley

0

No soy programador, pero sí ingeniero de factores humanos. Siento que un lenguaje de programación debería ser fácil de aprender, comprender y usar, y esto requiere que tenga una estructura lingüística simple y consistente. Aunque todos los idiomas son simbólicos y, por lo tanto, en su esencia, son arbitrarios, existen convenciones y su seguimiento hace que el lenguaje sea más fácil de aprender y usar.

Hay una gran cantidad de funciones en C ++ y otros lenguajes escritos como función (parámetro), una sintaxis que se usó originalmente para las relaciones funcionales en matemáticas en la era anterior a la computadora. printf()sigue esta sintaxis y si los escritores de C ++ quisieran crear algún método lógicamente diferente para leer y escribir archivos, simplemente podrían haber creado una función diferente usando una sintaxis similar.

En Python, por supuesto, podemos imprimir utilizando la object.methodsintaxis también bastante estándar , es decir, variablename.print, ya que las variables son objetos, pero en C ++ no lo son.

No me gusta la sintaxis de cout porque el operador << no sigue ninguna regla. Es un método o función, es decir, toma un parámetro y le hace algo. Sin embargo, está escrito como si fuera un operador matemático de comparación. Este es un enfoque pobre desde el punto de vista de los factores humanos.


-1

printfes una función mientras que coutes una variable.


66
Hice un retroceso porque, aunque la respuesta en sí misma puede ser incorrecta, sigue siendo una respuesta genuina. Si (correctamente) cree que la respuesta es incorrecta, tiene dos opciones: 1) agregar un comentario o 2) agregar una nueva respuesta (o hacer ambas cosas). No cambie la respuesta de alguien a tal que diga algo completamente diferente de lo que pretendía el autor.
Mark

1
printfes una función, pero printf()es una función call =)
vp_arth

cout es un objeto, no una variable.
Lin
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.