tipos leer imprimir dev datos caracteres cadena c string printf

leer - scanf



¿Varias llamadas a printf() frente a una llamada a printf() con una cadena larga? (5)

¿Cuáles son los costos incurridos por este estilo en comparación con tener varios printf () para cada línea?

Múltiple printf dará lugar a múltiples llamadas de función y esa es la única sobrecarga.

¿Habría un posible desbordamiento de pila si la cadena es demasiado larga?

No hay desbordamiento de pila en este caso. Los literales de cadena generalmente se almacenan en memoria de solo lectura, no en memoria de pila. Cuando se pasa una cadena a printf , solo se copia un puntero a su primer elemento en la pila.

El compilador tratará esta cadena de múltiples líneas "línea 1 / n"

"line 2/n" "line 3/n" "line 4/n" "line 5/n" "line 6/n" "line 7/n" "line 8/n" "line 9/n.. etc"

como una sola cuerda

"line 1/nline 2/nline 3/nline 4/nline 5/nline 6/nline 7/nline 8/nline 9/n.. etc"

y esto se almacenará en la sección de solo lectura de la memoria.

Pero tenga en cuenta que (señalado por pmg en un comment ) la sección 5.2.4.1 de la norma C11 dice que los límites de traducción dicen que

La implementación deberá poder traducir y ejecutar al menos un programa que contenga al menos una instancia de cada uno de los siguientes límites 18) :
[...]
- 4095 caracteres en una cadena literal (después de la concatenación)
[...]

Digamos que tengo una línea de printf() con una cadena larga:

printf( "line 1/n" "line 2/n" "line 3/n" "line 4/n" "line 5/n" "line 6/n" "line 7/n" "line 8/n" "line 9/n.. etc");

¿Cuáles son los costos incurridos por este estilo en comparación con tener varios printf() para cada línea?
¿Habría un posible desbordamiento de pila si la cadena es demasiado larga?


C concatena literales de cadena si están separados por nada o por espacios en blanco. Tan abajo

printf( "line 1/n" "line 2/n" "line 3/n" "line 4/n" "line 5/n" "line 6/n" "line 7/n" "line 8/n" "line 9/n.. etc");

Está perfectamente bien y se destaca en el punto de vista de la legibilidad. Además, una sola llamada a printfs sin duda tiene menos gastos generales que 9 llamadas a printf .


Cada printf adicional (o pone si su compilador lo optimiza de esa manera) incurrirá en la sobrecarga de la función específica del sistema cada vez, aunque hay una buena probabilidad de que la optimización los combine de todos modos.

Todavía no he visto una implementación de printf que fuera una función de hoja, por lo que espero gastos generales de llamada a funciones adicionales para algo como vfprintf y sus clientes.

Entonces es probable que tenga algún tipo de sobrecarga de llamadas al sistema para cada escritura. Como Printf usa stdout, que está almacenado en búfer, algunos de estos cambios de contexto (muy costosos) normalmente podrían evitarse ... excepto que todos los ejemplos anteriores terminan con nuevas líneas. La mayor parte de su costo probablemente estará aquí.

Si está realmente preocupado por el costo en su subproceso principal, mueva este tipo de cosas a un subproceso separado.


Un printf sin modificadores de formato se reemplaza silenciosamente (también conocido como. Optimizado) a una llamada de puts . Esto ya es una aceleración. Realmente no quieres perder eso al llamar a printf / puts varias veces.

GCC tiene printf (entre otros) como una función integrada, por lo que puede optimizar las llamadas durante el tiempo de compilación.

Ver:


printf es una función lenta si solo está generando cadenas constantes, porque printf tiene que escanear cada carácter para un especificador de formato ( % ). Las funciones como puts son significativamente más rápidas para cadenas largas, ya que, básicamente, solo pueden memcpy la cadena de entrada en el búfer de E / S de salida.

Muchos compiladores modernos (GCC, Clang, probablemente otros) tienen una optimización que convierte automáticamente printf en entradas si la cadena de entrada es una cadena constante sin especificadores de formato que termina con una nueva línea. Entonces, por ejemplo, compilando el siguiente código:

printf("line 1/n"); printf("line 2/n"); printf("line 3"); /* no newline */

da como resultado el siguiente ensamblaje (Clang 703.0.31, cc test.c -O2 -S ):

... leaq L_str(%rip), %rdi callq _puts leaq L_str.3(%rip), %rdi callq _puts leaq L_.str.2(%rip), %rdi xorl %eax, %eax callq _printf ...

en otras palabras, puts("line 1"); puts("line 2"); printf("line 3"); puts("line 1"); puts("line 2"); printf("line 3"); .

Si su printf larga de printf no termina con una nueva línea, entonces su rendimiento podría ser significativamente peor que si hubiera hecho un montón de llamadas a printf con cadenas terminadas en nueva línea, simplemente debido a esta optimización. Para demostrarlo, considere el siguiente programa:

#include <stdio.h> #define S "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" #define L S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S S /* L is a constant string of 4000 ''a''s */ int main() { int i; for(i=0; i<1000000; i++) { #ifdef SPLIT printf(L "/n"); printf(S); #else printf(L "/n" S); #endif } }

Si SPLIT no está definido (produciendo un solo printf sin una nueva línea de terminación), el tiempo se ve así:

[08/11 11:47:23] /tmp$ cc test.c -O2 -o test [08/11 11:47:28] /tmp$ time ./test > /dev/null real 0m2.203s user 0m2.151s sys 0m0.033s

Si se define SPLIT (que produce dos printf s, uno con una nueva línea de terminación, el otro sin), el tiempo se ve así:

[08/11 11:48:05] /tmp$ time ./test > /dev/null real 0m0.470s user 0m0.435s sys 0m0.026s

Como puede ver, en este caso, dividir el printf en dos partes en realidad produce una aceleración de 4x. Por supuesto, este es un caso extremo, pero ilustra cómo se puede optimizar de forma variable la impresión dependiendo de la entrada. (Tenga en cuenta que usar fwrite es incluso más rápido, 0.197, por lo que debería considerar usarlo si realmente quiere velocidad).

tl; dr: si está imprimiendo solo cadenas grandes y constantes, evite printf completo y use una función más rápida, como puts o fwrite .