Respuestas:
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 con qsort
(2 veces), en costo del tamaño ejecutable).
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, yscanf()
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 (usandoprintf()
yscanf()
):
- 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 aprintf()
yscanf()
?- Heredable: el
<iostream>
mecanismo de C ++ se construye a partir de clases reales comostd::ostream
ystd::istream
. A diferencia de<cstdio>
las deFILE*
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, printf
es significativamente más rápido, lo que puede justificar su uso con preferencia cout
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 /)
printf()
también se supone que es extensible. Ver "ganchos printf" en udrepper.livejournal.com/20948.html
printf
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:
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, 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 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 -lrt
la 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);
}
}
printf()
y std::ostream
es que el primero genera todos los argumentos en una sola llamada, mientras que std::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
o fprintf
y stringstream
o fstream
.
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).
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.
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 cout
es las opciones de formato. Formatear los datos (precisión, justificación, etc.) printf
es más fácil.
printf
a un archivo reemplazándolo con fprintf
...
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 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) 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
(y string
) si ya estoy usando el STL. De lo contrario, tiendo a evitarlo.
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.
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
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:
#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 printf
para 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!
thread
s no hace que la producción se vuelva loca. Acabo de reproducir y encontré ambos xyz
y ABC
en la salida. No hubo destrozos b / w ABC
como ABABAB
.
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 muestra AAA
y BBB
. Por favor, corríjalo, porque en este momento es confuso.
cout<< "Hello";
printf("%s", "Hello");
Ambos se utilizan para imprimir valores. Tienen una sintaxis completamente diferente. C ++ tiene ambos, C solo tiene printf.
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 usar printf
así:
Foo bar;
...;
printf("%s",bar);
puede ser posible, si Foo sobrecarga al buen operador. O si hiciste un buen método. En resumen, printf
es tan extensible como cout
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 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 printf
hay std::string::c_str()
, pero para scanf
?)
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
(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::string
no 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?)
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ómico
cout realiza la verificación de tipo, printf no.
No hay equivalente iostream de "% d"
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 la printf
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 malo printf
. 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 con printf
...
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 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.
printf
es una función mientras que cout
es una variable.
printf
es una función, pero printf()
es una función call =)