¿Se evalúan las expresiones C constantes en tiempo de compilación o en tiempo de ejecución?

7 minutos de lectura

avatar de usuario
Juez Maygarden

si escribo un #definir que realiza una operación utilizando otras constantes del preprocesador, ¿se calcula el valor final cada vez que aparece la macro en tiempo de ejecución? ¿Esto depende de las optimizaciones en el compilador o está cubierto por un estándar?

Ejemplo:

#define EXTERNAL_CLOCK_FREQUENCY    32768
#define TIMER_1_S                   EXTERNAL_CLOCK_FREQUENCY
#define TIMER_100_MS                TIMERB_1_S / 10

¿Será la operación 32768 / 10 ocurre en tiempo de ejecución cada vez que uso la macro TIMER_100_MS?

Me gustaría evitar lo siguiente:

#define EXTERNAL_CLOCK_FREQUENCY    32768
#define TIMER_1_S                   EXTERNAL_CLOCK_FREQUENCY
#define TIMER_100_MS                3276

Resumen

Se requiere un compilador para poder evaluar expresiones integrales constantes porque son necesarias para calcular cosas como tamaños de matrices en tiempo de compilación. Sin embargo, el estándar solo dice que “pueden”, no “deben”, hacerlo. Por lo tanto, solo un compilador con cerebro muerto no evaluaría expresiones integrales constantes en tiempo de compilación, pero una simple verificación de la salida del ensamblado para un compilador no convencional verificaría cada caso.

  • tenga en cuenta que TIMERB_1_S / 10 es 3276no 3277

    –MM

    4 de febrero de 2016 a las 0:08

Las macros son simplemente sustitución textual, por lo que en su ejemplo de escritura TIMER_100_MS en un programa es una forma elegante de escribir 32768 / 10.

Por lo tanto, la pregunta es cuándo evaluaría el compilador 32768 / 10, que es una expresión integral constante. No creo que el estándar requiera ningún comportamiento particular aquí (ya que la evaluación en tiempo de ejecución y en tiempo de compilación es indistinguible en efecto), pero cualquier compilador medianamente decente lo evaluará en tiempo de compilación.

  • Éste es el punto clave. El preprocesador manipula el texto, luego el compilador lo obtiene y sabe nada sobre cuánto preprocesamiento ha pasado antes…

    – dmckee — gatito ex-moderador

    12 de enero de 2009 a las 18:16

  • Incluso si el preprocesador lo hiciera, no puede hacerlo mientras no se defina de esta manera: #define TIMER_100_MS (TIMERB_1_S / 10) Dado que no entre paréntesis de la expresión, sería retorcido evaluarla para 3277 donde podrías haber escrito 1 / TIMER_100_MS (Donde el resultado de todos modos no sería lo que se desea, supongo) Pero si el preprocesador ya hubiera evaluado la declaración aquí, incluso rompería el orden de prioridad del operador.

    – dhein

    12 de febrero de 2015 a las 12:18

avatar de usuario
Johannes Schaub – litb

La mayoría de las respuestas aquí se centraron en el efecto de la macro sustitución. Pero creo que quería saber si

32768 / 10

se evalúa en tiempo de compilación. En primer lugar, esa es una expresión constante aritmética y, además, una expresión constante integral (porque solo tiene literales de tipo entero). La implementación es libre de calcularlo en tiempo de ejecución, pero también debe poder calcularlo en tiempo de compilación, porque

  1. debe dar un mensaje de diagnóstico si una expresión constante no es representable en el tipo que tiene su expresión
  2. tales expresiones están permitidas en contextos que requieren el valor en el momento de la traducción, por ejemplo, si se usan como el tamaño de una dimensión de matriz.

Si el compilador puede calcular principalmente el resultado ya en tiempo de compilación, debería usar ese valor y no volver a calcularlo en tiempo de ejecución, creo. Pero tal vez todavía hay alguna razón para hacer eso. no sabría

Editar: Lo siento, he respondido la pregunta como si fuera sobre C++. Noté que hoy preguntó sobre C. El desbordamiento en una expresión se considera un comportamiento indefinido en C, independientemente de si ocurre en una expresión constante o no. El segundo punto también es cierto en C, por supuesto.

Editar: Como señala un comentario, si la macro se sustituye por una expresión como 3 * TIMER_100_MSentonces esto evaluaría (3 * 32768) / 10. Por lo tanto, la respuesta simple y directa es “No, no ocurriría en tiempo de ejecución cada vez, porque la división puede no ocurrir en absoluto debido a las reglas de precedencia y asociatividad”. Mi respuesta anterior asume que la macro siempre se sustituye de modo que la división realmente ocurra.

  • Estrictamente hablando, esto está mal. Debido a la sustitución de macros, no puede saber a qué se evalúa esto, ya que importa lo que aparece junto a él después de la sustitución. Por ejemplo, 3 * 32768 / 10 puede ser diferente a 32768 / 10 * 3 en una expresión entera debido al orden de las operaciones de izquierda a derecha. Si la definición de macro rodeaba la expresión con ()entonces la respuesta sería verdadera.

    – Ladrillo

    13 de febrero de 2017 a las 21:06

  • @Brick pero en ese caso no es una “operación 32768/10”. Sin embargo, la pregunta era solo sobre la “operación 32768/10”. Entonces mi respuesta no dice nada sobre el caso “3 * 32768/10” porque es “(3 * 32768) / 10”. Mi respuesta es verdadera sobre “32768 / 10 * 3” y los otros casos donde existe tal operación.

    – Johannes Schaub – litb

    13 de febrero de 2017 a las 21:24


  • Esta es la pregunta, copiada directamente: “¿Ocurrirá la operación 32768/10 en tiempo de ejecución cada vez que use la macro TIMER_100_MS?” Definitivamente se trata del uso de la macro, y la macro se evalúa sin el paréntesis. No puede saber si esto simplifica o no a menos que conozca todos los lugares donde se usa la macro.

    – Ladrillo

    13 de febrero de 2017 a las 21:26

No conozco ningún estándar que garantice que se optimizará. El preprocesador sustituirá 32768/10 por TIMER_100_MS, que puede ver ejecutando gcc -c. Para ver si el compilador se está optimizando aún más, ejecute gcc -S y verifique el ensamblador. Con gcc 4.1, incluso sin banderas de optimización, esto se reduce a la constante durante la compilación:

#include <stdlib.h>
#include <stdio.h>

#define EXTERNAL_CLOCK_FREQUENCY    32768
#define TIMER_1_S                   EXTERNAL_CLOCK_FREQUENCY
#define TIMER_100_MS                TIMER_1_S / 10

int main(int argc, char **argv)
{
  printf("%d\n", TIMER_100_MS);

  return(0);
}

gcc -S test.c
cat test.s

...
    popl    %ebx
    movl    $3276, 4(%esp)
    leal    LC0-"L00000000001$pb"(%ebx), %eax
    movl    %eax, (%esp)
    call    L_printf$stub
...

  • Con esta técnica, puede demostrar que incluso operaciones matemáticas complicadas, como log(3.0) se traducirá a valores inmediatos en línea, aunque el desmontaje codificará log(3.0) como 4607626529066517259 que es la conversión de enteros de 64 bits del equivalente IEEE doble…

    –Mark Lakata

    17 mayo 2016 a las 23:55

El compilador debería optimizar esa expresión. No creo que el estándar lo requiera, pero nunca he visto un compilador que NO realice esa tarea.

Sin embargo, NO debe escribir:

#define TIMER_100_MS      TIMERB_1_S / 10

… porque eso es un error esperando a suceder. Siempre debe poner entre paréntesis #defines que involucran expresiones.

#define TIMER_100_MS      (TIMERB_1_S / 10)

Considerar :

i = 10 * TIMER_100_MS;

El primer caso daría 32768 ((10*TIMERB_1_S)/10), el segundo 32760 (10*(TIMERB_1_S/10)). No es una diferencia crítica aquí, ¡pero DEBE ser consciente de ello!

avatar de usuario
Juez Maygarden

Desde el Borrador del Comité WG14/N1124 — 6 de mayo de 2005 ISO/IEC 9899:TC2:

6.6 Expresiones constantes

Sintaxis

expresión-constante:
expresión condicional

Descripción

Una expresión constante se puede evaluar durante la traducción en lugar del tiempo de ejecución y, en consecuencia, se puede usar en cualquier lugar donde se encuentre una constante.

Restricciones

Las expresiones constantes no deben contener operadores de asignación, incremento, decremento, llamada de función o coma, excepto cuando están contenidos dentro de una subexpresión que no se evalúa.96)

Cada expresión constante se evaluará como una constante que esté en el rango de valores representables para su tipo.

  • Gracias por citar su fuente, es un recurso valioso.

    – Enrique

    30 de marzo de 2016 a las 19:26

avatar de usuario
Bill el lagarto

¿Ocurrirá la operación 32768/10 en tiempo de ejecución cada vez que use el TIMERB_100_MS ¿macro?

Cada lugar en su código donde usa TIMERB_100_MSserá reemplazado por 32768 / 10 por el preprocesador.

Si esa expresión se optimiza aún más (se evalúa como una constante) depende de su compilador.

  • Gracias por citar su fuente, es un recurso valioso.

    – Enrique

    30 de marzo de 2016 a las 19:26

avatar de usuario
norman ramsey

Amigos, esta transformación se llama “plegamiento constante” e incluso la mayoría de los estudiantes compiladores lo hacen. Siempre que tenga un compilador creado por alguien que no sea usted o su compañero de cuarto de la universidad y esté compilando un lenguaje escrito estáticamente, puede contar con él incluso sin la optimización activada. Es un asunto diferente si se trata de un lenguaje dinámico extravagante que puede cambiar el significado de /.

  • ¡Plegado constante! eso es lo que estaba buscando.

    – usuario2387149

    4 oct 2016 a las 21:06

¿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