¿Es “char foo = 255” un comportamiento indefinido si se firma char?

Lo siguiente no me da ningún tipo de advertencia cuando se comstack con gcc 4.5.2 en una máquina x86 con Linux:

char foo = 255; 

Pero cuando uso -pedantic , gcc dice:

advertencia: desbordamiento en conversión constante implícita

La forma en que gcc actúa es un poco extraño y me hace dudar si realmente entiendo lo que está pasando en esta tarea. Creo que si char es de 8 bits en POSIX y está firmado de forma predeterminada, no puede contener 255 .

En el estándar C, dice que el desbordamiento de enteros sin signo da lugar a desbordamiento, pero el desbordamiento de enteros con signo no está definido. Entonces, ¿esta asignación es un comportamiento indefinido? ¿Y por qué gcc actúa de esta manera?

Resumen: El resultado está definido por la implementación y es muy probable que sea -1 , pero es complicado, al menos en principio.

Las reglas relativas al desbordamiento son diferentes para los operadores frente a las conversiones, y para los tipos firmados frente a los tipos no firmados, y las reglas de conversión cambiaron entre C90 y C99.

A partir de C90, el desbordamiento de un operador con operandos enteros con signo (“desbordamiento” significa que el resultado matemático no se puede representar en el tipo de expresión) tiene un comportamiento indefinido. Para los operandos enteros sin signo, el comportamiento está bien definido como el envolvimiento habitual (estrictamente hablando, el estándar no lo denomina “desbordamiento”). Pero tu statement:

 char foo = 255; 

no utiliza ningún operador (el = es un inicializador, no una asignación), así que nada de eso se aplica en este caso.

Si el tipo char puede representar el valor 255 (que es cierto si cualquiera de los caracteres simples no está firmado o si CHAR_BIT >= 9 ), entonces, por supuesto, el comportamiento está bien definido. La expresión int 255 se convierte implícitamente a char . (Dado que CHAR_BIT >= 8 , no es posible que este caso particular invoque envoltura sin firmar).

De lo contrario, la conversión produce un resultado que no se puede almacenar en un char .

A partir de C90, el resultado de la conversión está definido por la implementación, lo que significa que está garantizado que establezca foo en algún valor dentro del rango de tipo char , y puede determinar cuál es ese valor leyendo la documentación de la implementación, que se requiere para decirle cómo funciona la conversión. (Nunca he visto una implementación donde el valor almacenado sea distinto de -1 , pero en principio cualquier resultado es posible).

C99 cambió la definición, de modo que una conversión desbordada a un tipo firmado produce un resultado definido por la implementación o genera una señal definida por la implementación.

Si un comstackdor elige hacer esto último, entonces debe documentar qué señal se genera.

Entonces, ¿qué sucede si se genera una señal definida por la implementación? La sección 7.14 de la norma dice:

El conjunto completo de señales, su semántica y su manejo predeterminado están definidos por la implementación

No está del todo claro (para mí) cuál es el rango de posibles comportamientos para el “manejo predeterminado” de las señales. En el peor de los casos, supongo que tal señal podría terminar el progtwig. Puede o no ser capaz de definir un controlador de señal que atrapa la señal.

7.14 también dice:

Si y cuando la función devuelve, si el valor de sig es SIGFPE , SIGILL , SIGSEGV o cualquier otro valor definido por la implementación que corresponda a una excepción computacional, el comportamiento no está definido; De lo contrario, el progtwig reanudará la ejecución en el punto en que se interrumpió.

pero no creo que se aplique, ya que una conversión desbordada no es una “excepción computacional” como se usa el término aquí. (A menos que la señal definida por la implementación sea SIGFPE , SIGILL o SIGSEGV , pero eso sería una tontería).

Por lo tanto, en última instancia, si una implementación elige emitir una señal en respuesta a una conversión desbordada, el comportamiento (no solo el resultado) es al menos definido por la implementación, y puede haber circunstancias en las que podría no estar definido. En cualquier caso, no parece haber una manera portátil de lidiar con tal señal.

En la práctica, nunca he oído hablar de una implementación que aproveche la nueva redacción en C99. Para todos los comstackdores de los que he oído hablar, el resultado de la conversión está definido por la implementación, y es muy probable que produzca lo que cabría esperar de un truncamiento de complemento a 2. (Y no estoy del todo convencido de que este cambio en C99 fue una buena idea. En todo caso, hizo que esta respuesta fuera 3 veces más larga de lo que hubiera sido necesario).

El desbordamiento de enteros con signo causa un comportamiento indefinido solo cuando ocurre cuando se evalúan resultados intermedios de expresiones aritméticas, por ejemplo, durante la mutilación binaria, la disminución unaria, etc.

Una conversión integral desbordada a tipo firmado no causa un comportamiento indefinido. En su lugar, produce un resultado definido por la implementación (con la posibilidad de que se genere una señal definida por la implementación).

Según C11, 6.3.1.3:

Cuando un valor con tipo entero se convierte en otro tipo entero distinto de _Bool , si el nuevo tipo está […] firmado y el valor no puede representarse en él; o bien el resultado está definido por la implementación o se genera una señal definida por la implementación.