¿Puede el código que nunca se ejecutará invocar un comportamiento indefinido?

El código que invoca un comportamiento indefinido (en este ejemplo, división por cero) nunca se ejecutará, ¿el progtwig sigue siendo un comportamiento indefinido?

int main(void) { int i; if(0) { i = 1/0; } return 0; } 

Creo que aún es un comportamiento indefinido, pero no puedo encontrar ninguna evidencia en el estándar para apoyarme o negarme.

Entonces, ¿alguna idea?

Veamos cómo el estándar C define los términos “comportamiento” y “comportamiento indefinido”.

Las referencias son al borrador N1570 de la norma ISO C 2011; No tengo conocimiento de diferencias relevantes en ninguna de las tres normas ISO C publicadas (1990, 1999 y 2011).

Sección 3.4:

comportamiento
Apariencia externa o acción

Ok, eso es un poco vago, pero diría que una statement dada no tiene “apariencia”, y ciertamente no hay “acción”, a menos que se ejecute realmente.

Sección 3.4.3:

comportamiento indefinido
comportamiento, en el uso de una construcción de progtwig no portátil o errónea o de datos erróneos, para los cuales esta Norma Internacional no impone requisitos

Dice ” sobre el uso ” de tal construcción. La palabra “uso” no está definida por el estándar, por lo que recurrimos al significado común en inglés. Una construcción no se “usa” si nunca se ejecuta.

Hay una nota bajo esa definición:

NOTA El posible comportamiento indefinido abarca desde ignorar la situación completamente con resultados impredecibles, hasta comportarse durante la traducción o la ejecución del progtwig de una manera documentada característica del entorno (con o sin la emisión de un mensaje de diagnóstico), hasta terminar una traducción o ejecución (con la emisión de un mensaje de diagnóstico).

Por lo tanto, un comstackdor tiene permitido rechazar su progtwig en tiempo de comstackción si su comportamiento no está definido. Pero mi interpretación de eso es que puede hacerlo solo si puede probar que cada ejecución del progtwig se encontrará con un comportamiento indefinido. Lo que implica, creo, que esto:

 if (rand() % 2 == 0) { i = i / 0; } 

que ciertamente puede tener un comportamiento indefinido, no puede ser rechazado en tiempo de comstackción.

Como cuestión práctica, los progtwigs deben poder realizar pruebas de tiempo de ejecución para evitar invocar comportamientos indefinidos, y la norma debe permitirles hacerlo.

Tu ejemplo fue:

 if (0) { i = 1/0; } 

que nunca ejecuta la división por 0. Un modismo muy común es:

 int x, y; /* set values for x and y */ if (y != 0) { x = x / y; } 

La división ciertamente tiene un comportamiento indefinido si y == 0 , pero nunca se ejecuta si y == 0 . El comportamiento está bien definido, y por la misma razón que su ejemplo está bien definido: porque el comportamiento potencial indefinido nunca puede suceder realmente.

(A menos que INT_MIN < -INT_MAX && x == INT_MIN && y == -1 (sí, la división de enteros puede desbordarse), pero eso es un problema aparte).

En un comentario (desde que se eliminó), alguien señaló que el comstackdor puede evaluar expresiones constantes en el momento de la comstackción. Lo que es cierto, pero no relevante en este caso, porque en el contexto de

 i = 1/0; 

1/0 no es una expresión constante .

Una expresión constante es una categoría sintáctica que se reduce a una expresión condicional (que excluye asignaciones y expresiones de coma). La expresión constante de producción aparece en la gramática solo en contextos que realmente requieren una expresión constante, como las tags de casos. Así que si escribes:

 switch (...) { case 1/0: ... } 

entonces 1/0 es una expresión constante, y una que viola la restricción en 6.6p4: "Cada expresión constante se evaluará a una constante que esté en el rango de valores representables para su tipo", por lo que se requiere un diagnóstico. Pero el lado derecho de una asignación no requiere una expresión constante , simplemente una expresión condicional , por lo que las restricciones en las expresiones constantes no se aplican. Un comstackdor puede evaluar cualquier expresión que sea capaz de comstackr, pero solo si el comportamiento es el mismo que si fuera evaluado durante la ejecución (o, en el contexto de if (0) , no evaluado durante la ejecución ().

(Algo que se ve exactamente como una expresión constante no es necesariamente una expresión constante , al igual que, en x + y * z , la secuencia x + y no es una expresión aditiva debido al contexto en el que aparece).

Lo que significa la nota a pie de página en la sección 6.6 de N1570 que iba a citar:

Así, en la siguiente inicialización,
static int i = 2 || 1 / 0;
la expresión es una expresión constante de entero válida con valor uno.

no es realmente relevante para esta pregunta

Finalmente, hay algunas cosas que se definen para causar un comportamiento indefinido que no se trata de lo que sucede durante la ejecución. El anexo J, sección 2 de la norma C (nuevamente, vea el borrador de N1570 ) enumera las cosas que causan un comportamiento indefinido, recogidas del rest de la norma. Algunos ejemplos (no reclamo que esta sea una lista exhaustiva) son:

  • Un archivo fuente no vacío no termina en un carácter de nueva línea que no está precedido inmediatamente por un carácter de barra invertida o termina en un token o comentario de preprocesamiento parcial
  • La concatenación de token produce una secuencia de caracteres que coincide con la syntax de un nombre de carácter universal
  • Un carácter que no se encuentra en el conjunto de caracteres de origen básico se encuentra en un archivo de origen, excepto en un identificador, una constante de caracteres, un literal de cadena, un nombre de encabezado, un comentario o un token de preproceso que nunca se convierte en un token
  • Un identificador, comentario, cadena literal, constante de caracteres o nombre de encabezado contiene un carácter multibyte no válido o no comienza y finaliza en el estado de cambio inicial
  • El mismo identificador tiene enlaces internos y externos en la misma unidad de traducción

Estos casos particulares son cosas que un comstackdor podría detectar. Creo que su comportamiento no está definido porque el comité no quiso, o no pudo, imponer el mismo comportamiento en todas las implementaciones, y definir un rango de comportamientos permitidos simplemente no valió la pena. Realmente no entran en la categoría de "código que nunca se ejecutará", pero los menciono aquí para completar.

Este artículo discute esta pregunta en la sección 2.6:

 int main(void){ guard(); 5 / 0; } 

Los autores consideran que el progtwig se define cuando guard() no termina. También se encuentran con nociones distintivas de “estáticamente indefinido” y “dinámicamente indefinido”, por ejemplo:

La intención detrás del estándar 11 parece ser que, en general, las situaciones se hacen estáticamente indefinidas si no es fácil generar código para ellas. Solo cuando se puede generar código, entonces la situación puede ser indefinida dinámicamente.

11) Correspondencia privada con miembro del comité.

Recomendaría mirar el artículo completo. Tomados juntos, pinta una imagen consistente.

El hecho de que los autores del artículo hayan tenido que discutir la pregunta con un miembro del comité confirma que la norma actualmente está difusa en la respuesta a su pregunta.

En este caso, el comportamiento indefinido es el resultado de ejecutar el código. Entonces, si el código no se ejecuta, no hay un comportamiento indefinido.

El código no ejecutado podría invocar un comportamiento indefinido si el comportamiento indefinido fue el resultado de únicamente la statement del código (por ejemplo, si algún caso de variable sombreado no estaba definido).

Me gustaría ir con el último párrafo de esta respuesta: https://stackoverflow.com/a/18384176/694576

… UB es un problema de tiempo de ejecución, no un problema de tiempo de comstackción …

Entonces, no, no se invoca a UB.

Solo cuando el estándar hace cambios de ruptura y su código de repente ya no es “nunca se ejecuta”. Pero no veo ninguna forma lógica en la que esto pueda causar un “comportamiento indefinido”. No está causando nada .

Sobre el tema del comportamiento indefinido, a menudo es difícil separar los aspectos formales de los prácticos. Esta es la definición de comportamiento indefinido en el estándar de 1989 (no tengo una versión más reciente a la mano, pero no espero que esto haya cambiado sustancialmente):

 1 comportamiento indefinido
   comportamiento, sobre el uso de una construcción de progtwig no portátil o errónea o de
   Datos erróneos, para los cuales esta Norma Internacional no impone requisitos.
 2 NOTA El posible comportamiento indefinido varía desde ignorar la situación completamente
   Con resultados impredecibles, para comportarse durante la traducción o la ejecución del progtwig.
   de una manera documentada característica del medio ambiente (con o sin el
   emisión de un mensaje de diagnóstico), para terminar una traducción o
   Ejecución (con la emisión de un mensaje de diagnóstico).

Desde un punto de vista formal, diría que su progtwig invoca un comportamiento indefinido, lo que significa que el estándar no impone ningún requisito sobre lo que hará cuando se ejecute, solo porque contiene división por cero.

Por otro lado, desde un punto de vista práctico, me sorprendería encontrar un comstackdor que no se comporte como usted espera intuitivamente.

La norma dice que, como recuerdo bien, está permitido hacer cualquier cosa desde el momento, se rompió una regla. Tal vez hay algunos eventos especiales con un tipo de sabor global (pero nunca escuché o leí algo así) … Entonces diría: No, esto no puede ser UB, porque siempre que el comportamiento esté bien definido, 0 es siempre falso, por lo que la regla no puede romperse en tiempo de ejecución.

Creo que aún es un comportamiento indefinido, pero no puedo encontrar ninguna evidencia en el estándar para apoyarme o negarme.

Creo que el progtwig no invoca un comportamiento indefinido.

El Informe de defectos # 109 aborda una pregunta similar y dice:

Además, si cada ejecución posible de un progtwig dado resultara en un comportamiento indefinido, el progtwig dado no es estrictamente conforme. Una implementación conforme no debe fallar en traducir un progtwig estrictamente conforme simplemente porque alguna posible ejecución de ese progtwig resultaría en un comportamiento indefinido. Debido a que foo nunca podría ser llamado, el ejemplo dado debe ser traducido exitosamente por una implementación conforme.

Depende de cómo se define la expresión “comportamiento indefinido”, y si “comportamiento indefinido” de una statement es lo mismo que “comportamiento indefinido” para un progtwig.

Este progtwig se parece a C, por lo que es apropiado un análisis más profundo de lo que es el estándar de C utilizado por el comstackdor (como lo hicieron algunas respuestas).

En ausencia de un estándar especificado, la respuesta correcta es “depende”. En algunos lenguajes, los comstackdores después del primer error intentan adivinar lo que podría significar el progtwigdor y aún generan algo de código, según la conjetura de los comstackdores. En otros lenguajes más puros, una vez que algo no está definido, la indefinición se propaga a todo el progtwig.

Otros idiomas tienen un concepto de “errores limitados”. Para algunos tipos limitados de errores, estos idiomas definen la cantidad de daño que puede producir un error. En particular, los idiomas con recolección de basura implícita frecuentemente hacen una diferencia si un error invalida el sistema de escritura o no.