El compilador cambia printf a puts

5 minutos de lectura

El compilador cambia printf a puts
skgbanga

Considere el siguiente código:

#include <stdio.h>

void foo() {
    printf("Hello world\n");
}

void bar() {
    printf("Hello world");
}

El ensamblaje producido por estas dos funciones es:

.LC0:
        .string "Hello world"
foo():
        mov     edi, OFFSET FLAT:.LC0
        jmp     puts
bar():
        mov     edi, OFFSET FLAT:.LC0
        xor     eax, eax
        jmp     printf

Ahora sé la diferencia entre puts y printf, pero me parece bastante interesante que gcc pueda introspeccionar el const char* y decidir si llamar a printf o puts.

Otra cosa interesante es que en barel compilador puso a cero el registro de retorno (eax) a pesar de que es un void función. ¿Por qué hizo eso allí y no en foo?

¿Tengo razón al suponer que el compilador ‘introspeccionó mi cadena’, o hay otra explicación de esto?

  • Por cierto: un buen compilador inspecciona la cadena de formato de todos modos, ya que verifica el número y los tipos de los parámetros proporcionados y emite una advertencia si hay una falta de coincidencia.

    – Gerhardh

    5 de febrero de 2020 a las 16:27

  • @Gerhardh, el compilador necesitaría inspeccionar la cadena para detectar la presencia de la nueva línea.

    – Veleta

    5 de febrero de 2020 a las 16:27

  • @Gerhardh puts genera una nueva línea después de la cadena, por lo que si no hay una, no se puede sustituir.

    – Veleta

    5 de febrero de 2020 a las 16:30


  • @Gerhard puts no verifica nada: genera lo que le das, pero también hay otra diferencia de tiempo de ejecución. si paso un NULL puntero como una cadena a printfMSVC muestra amablemente (pero no tiene que hacerlo) (null) pero con puts se estrella

    – Veleta

    5 de febrero de 2020 a las 16:34

  • Esto no es introspección de la cuerda; el compilador no se mira a sí mismo. Es una simple y vieja inspección.

    –Eric Postpischil

    5 de febrero de 2020 a las 17:26


1647629346 744 El compilador cambia printf a puts
marco bonelli

¿Tengo razón al suponer que el compilador ‘introspeccionó mi cadena’, o hay otra explicación de esto?

Sí, esto es exactamente lo que sucede. Es una optimización bastante simple y común realizada por el compilador.

Desde tu primera printf() la llamada es solo:

printf("Hello world\n");

es equivalente a:

puts("Hello world");

Ya que puts() no necesita escanear y analizar la cadena en busca de especificadores de formato, es bastante más rápido que printf(). El compilador nota que su cadena termina con una nueva línea y no contiene especificadores de formato y, por lo tanto, convierte automáticamente la llamada.

Esto también ahorra un poco de espacio, ya que ahora solo una cadena "Hello world" debe almacenarse en el binario resultante.

Tenga en cuenta que esto no es posible en general para llamadas de la forma:

printf(some_var);

Si some_var no es una cadena constante simple, el compilador no puede saber si termina en \n.

Otras optimizaciones comunes son:

  • strlen("constant string") podría evaluarse en tiempo de compilación y convertirse en un número.
  • memmove(location1, location2, sz) podría transformarse en memcpy() si el compilador está seguro de que location1 y location2 no se superpongan.
  • memcpy() de pequeños tamaños se pueden convertir en un solo mov instrucción, e incluso si el tamaño es mayor, la llamada a veces se puede insertar para que sea más rápida.

Otra cosa interesante es que en barel compilador puso a cero el registro de retorno (eax) a pesar de que es un void función. ¿Por qué hizo eso allí y no en foo?

Vea aquí: ¿Por qué %eax se pone a cero antes de una llamada a printf?


Publicaciones interesantes relacionadas

  • ¿Por qué GCC no optimiza esta llamada a printf?
  • ¿Se puede reemplazar printf por puts automáticamente en un programa C?
  • ¿Por qué muestra puts cuando desensamblo sin importar si estoy usando printf o puts?
  • Diferencia entre printf@plt y puts@plt
  • -O2 optimiza printf(“%s\n”, str) a puts(str)

  • Gracias. Cualquier pista con respecto a la puesta a cero del eax ¿Registrarse?

    – skgbanga

    5 de febrero de 2020 a las 16:35

  • @skgbanga puts() y printf() tienen significados de retorno ligeramente diferentes.

    – chux – Reincorporar a Monica

    5 de febrero de 2020 a las 16:37


  • @klutt OP se refería a bar allí.

    – Marco Bonelli

    5 de febrero de 2020 a las 16:49

  • @MarcoBonelli: Dato curioso: los compiladores también saben cómo optimizar printf("%s\n", string) en pone. El mecanismo es que printf se trata como una función incorporada.

    – Peter Cordes

    5 de febrero de 2020 a las 17:18

  • Enlaces relacionados, no duplicados exactos: ¿Por qué GCC no optimiza esta llamada a printf? tiene más detalles sobre lo que GCC puede/no puede hacer, y un enlace a un artículo al respecto. ¿Se puede reemplazar printf por puts automáticamente en un programa C? es una variante de esto. ¿Por qué muestra puts cuando desensamblo sin importar si estoy usando printf o puts? es un dup de esto con una respuesta menos detallada, lo cerré. También diferencia entre printf@plt y puts@plt

    – Peter Cordes

    5 de febrero de 2020 a las 17:25


Otra cosa interesante es que en bar, el compilador puso a cero el registro de retorno (eax) a pesar de que es una función nula. ¿Por qué hizo eso allí y no en foo?

Esto no tiene nada que ver con la pregunta del título, pero no obstante es interesante.

La reducción a cero de xor %eax es antes de la llamada a printf es parte de la llamada y no tiene nada que ver con el valor de retorno. La razón por la que esto sucede es que printf es una función varargs, y la función ABI x86_64 para varargs requiere pasar argumentos de coma flotante en registros xmm, y requiere pasar el número de tales argumentos en %al. Entonces, esta instrucción está ahí para garantizar que %al sea 0, ya que no se pasan argumentos en los registros xmm a printf.

puts no es una función varargs, por lo que no se requiere allí.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad