Me sorprende que todos en esta pregunta afirmen que std::cout
es mucho mejor que eso printf
, incluso si la pregunta solo pide diferencias. Ahora, hay una diferencia: std::cout
es C ++ y printf
es C (sin embargo, puede usarlo en C ++, como casi cualquier otra cosa de C). Ahora, seré honesto aquí; ambos printf
y std::cout
tienen sus ventajas.
std::cout
Es extensible. Sé que la gente dirá que también printf
es 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::cout
depende 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::ostream
como 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::ostream
sobrecarga 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.
Como se pudo observar con facilidad, tanto printf
y std::cout
usar sintaxis diferente. printf
usa la sintaxis de funciones estándar usando cadenas de patrones y listas de argumentos de longitud variable. En realidad, printf
es una razón por la cual C los tiene: los printf
formatos son demasiado complejos para ser utilizables sin ellos. Sin embargo, std::cout
utiliza 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::endl
realidad 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í 0x0424
es una locura. Esto es causado por la std::cout
mezcla de estado y valores reales. Nunca vi un lenguaje en el que algo así std::setfill
fuera un tipo (aparte de C ++, por supuesto). printf
separa claramente los argumentos y el tipo real. Realmente preferiría mantener la printf
versión (incluso si parece un poco críptica) en comparación con la iostream
versió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;
Aquí es donde la verdadera ventaja de las printf
mentiras. La printf
cadena 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 iostream
construcciones a las printf
que 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.
C tiene muchos tipos enteros, y también C ++. std::cout
maneja todos los tipos por usted, mientras que printf
requiere 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 printf
es const char *
(cadena C, se puede obtener usando el to_c
método de std::string
)). Por ejemplo, para imprimir size_t
, necesita usar %zd
, mientras int64_t
que 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 .
\0
Debido a que printf
usa cadenas C en lugar de cadenas C ++, no puede imprimir un byte NUL sin trucos específicos. En ciertos casos es posible utilizar %c
con '\0'
como argumento, aunque eso es claramente un truco.
Actualización: Resulta que iostream
es tan lento que generalmente es más lento que su disco duro (si redirige su programa a un archivo). Deshabilitar la sincronización con stdio
puede 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 printf
o iostream
. Creo que printf
podría ser más rápido desde un vistazo rápido al ensamblaje (compilado con clang usando la -O3
opción del compilador). Suponiendo que mi ejemplo de error, printf
ejemplo hace menos llamadas que el cout
ejemplo. Esto es int main
con 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 printf
argumentos. Eso es todo; no hay nada más. A modo de comparación, esto se iostream
compila 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 iostream
no es más rápido porque es "tipo seguro". La mayoría de las implementaciones de C implementan printf
formatos que usan goto computarizado, por lo que printf
es lo más rápido posible, incluso sin que el compilador lo sepa printf
(no es que no lo sean, algunos compiladores pueden optimizar printf
en ciertos casos, la secuencia constante que termina \n
generalmente está optimizada para puts
) .
No sé por qué querrías heredar ostream
, pero no me importa. Es posible con FILE
también.
class MyFile : public FILE {}
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 printf
cadena 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);
^
std::sort
, que de alguna manera es sorprendentemente rápida en comparación conqsort
(2 veces), en costo del tamaño ejecutable).De las preguntas frecuentes de C ++ :
Por otro lado,
printf
es significativamente más rápido, lo que puede justificar su uso con preferenciacout
en 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 /)fuente
printf()
también se supone que es extensible. Ver "ganchos printf" en udrepper.livejournal.com/20948.htmlprintf
no tiene esa habilidad. Los mecanismos de biblioteca no portátiles apenas están al mismo nivel que la extensibilidad completamente estandarizada de iostreams.Las personas a menudo afirman que
printf
es mucho más rápido. Esto es en gran parte un mito. Lo acabo de probar con los siguientes resultados:Conclusión: si solo desea nuevas líneas, use
printf
; de lo contrario,cout
es 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
iostream
siempre son mejores queprintf
; 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-lrt
la sincronización).fuente
printf()
ystd::ostream
es que el primero genera todos los argumentos en una sola llamada, mientras questd::ostream
incurre 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.printf
podrí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.sprintf
ofprintf
ystringstream
ofstream
.Y cito :
fuente
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.fuente
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 :)
3) Encuentro cout más legible, especialmente cuando tenemos muchos parámetros.
Un problema con
cout
es las opciones de formato. Formatear los datos (precisión, justificación, etc.)printf
es más fácil.fuente
printf
a un archivo reemplazándolo confprintf
...Dos puntos no mencionados aquí que encuentro significativos:
1)
cout
lleva mucho equipaje si aún no está utilizando el STL. Agrega más del doble de código a su archivo objeto queprintf
. Esto también es cierto parastring
, y esta es la razón principal por la que tiendo a usar mi propia biblioteca de cadenas.2)
cout
utiliza<<
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
(ystring
) si ya estoy usando el STL. De lo contrario, tiendo a evitarlo.fuente
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,
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.
fuente
Por supuesto, puede escribir "algo" un poco mejor para mantener el mantenimiento:
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):
El resultado es:
fuente
endl
por qué es mucho menos eficiente que'\n'
?endl
vacía el búfer y\n
no lo hace, aunque no estoy seguro de que esta sea definitivamente la razón.Me gustaría señalar que si quieres jugar con hilos en C ++, si lo
cout
usas puedes obtener algunos resultados interesantes.Considera este código:
Ahora, el resultado viene todo mezclado. También puede producir resultados diferentes, intente ejecutar varias veces:
Puede usarlo
printf
para hacerlo bien, o puede usarlomutex
.¡Que te diviertas!
fuente
thread
s no hace que la producción se vuelva loca. Acabo de reproducir y encontré ambosxyz
yABC
en la salida. No hubo destrozos b / wABC
comoABABAB
.cout
funciona 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 muestraAAA
yBBB
. Por favor, corríjalo, porque en este momento es confuso.Ambos se utilizan para imprimir valores. Tienen una sintaxis completamente diferente. C ++ tiene ambos, C solo tiene printf.
fuente
Me gustaría decir que la falta de extensibilidad no
printf
es 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 usarprintf
así:puede ser posible, si Foo sobrecarga al buen operador. O si hiciste un buen método. En resumen,
printf
es tan extensible comocout
para 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 usoputchar('\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
(porqueprintf
haystd::string::c_str()
, pero parascanf
?)Por lo
printf
que 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
%n
formateador: ". 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
(yscanf
) 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 questd::string
no es compatible. Tenemos que pasar por unchar*
antes de dárseloprintf
(con elstd::string::c_str()
si queremos leer, pero ¿cómo escribir?)fuente
char*
No se utilizará una conversión definida por el usuario .char*
vive y por cuánto tiempo, y los peligros de la definición del usuario moldes implícitos.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ómicocout realiza la verificación de tipo, printf no.
No hay equivalente iostream de
"% d"
fuente
cout
no 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 laprintf
llamada "atómica"?printf("%s\n",7);
%s
es ?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 maloprintf
. Las herramientas de análisis estático pueden detectar muchos de estos problemas.printf
no realiza la verificación de tipos, nunca he usado un compilador que no me haya advertido sobre errores de tipo conprintf
...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:
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:
Programa C ++:
Hice mi mejor esfuerzo para pulirlos antes de enlazarlos 100,000 veces. Aquí están los resultados:
Programa C:
Programa C ++:
Tamaño del archivo de objeto:
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.
fuente
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.method
sintaxis 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.
fuente
printf
es una función mientras quecout
es una variable.fuente
printf
es una función, peroprintf()
es una función call =)