online c gcc

online - ¿Es el comportamiento indefinido "char foo=255" si se firma char?



gcc flags list (3)

De acuerdo con C11, 6.3.1.3:

Cuando un valor con tipo entero se convierte a otro tipo entero distinto de _Bool , si [...] el nuevo tipo se firma y el valor no se puede representar en él; el resultado es definido por la implementación o se genera una señal definida por la implementación.

Lo siguiente no me da ninguna advertencia cuando se compila con gcc 4.5.2 en la 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 actúa gcc es un poco extraña y me hace dudar si realmente entiendo lo que está pasando en esta tarea. Creo que si el char es 8 bits largo en POSIX y está firmado por defecto, no puede contener 255 .

En el estándar C, indica que el desbordamiento de entero sin signo genera un desbordamiento, pero el desbordamiento de entero con signo no está definido. Entonces, ¿esta tarea es un comportamiento indefinido? ¿Y por qué gcc actúa de esta manera?


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 mutiplicación binaria, decremento unario, etc. Además, la conversión del valor de punto flotante a tipo entero causa un comportamiento indefinido si el valor está fuera de rango.

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


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 con respecto al desbordamiento son diferentes para los operadores frente a las conversiones, y para los tipos firmados frente a los tipos sin firmar, 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", lo que significa que el resultado matemático no se puede representar en el tipo de expresión) tiene un comportamiento indefinido. Para los operandos de enteros sin signo, el comportamiento está bien definido como el envolvente habitual (estrictamente hablando, el estándar no lo llama un "desbordamiento"). Pero tu declaración:

char foo = 255;

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

Si el tipo char puede representar el valor 255 (que es verdadero, ya sea sin char o CHAR_BIT >= 9 ), entonces, por supuesto, el comportamiento está bien definido. La expresión int 255 se convierte implícitamente en char . (Dado que CHAR_BIT >= 8 , no es posible para este caso en particular invocar wraparound sin signo).

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 establecer foo a algún valor dentro del rango de tipo char , y usted puede determinar cuál es ese valor leyendo la documentación de la implementación, que es obligatoria para decirte cómo funciona la conversión (Nunca he visto una implementación en la que el valor almacenado sea distinto de -1 , pero cualquier resultado es posible, en principio).

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

Si un compilador 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á definido por la implementación

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

7.14 también dice:

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

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

Entonces, en última instancia, si una implementación elige generar una señal en respuesta a una conversión de desbordamiento, el comportamiento (no solo el resultado) está definido al menos por la implementación, y puede haber circunstancias en las que podría no estar definido. En cualquier caso, no parece haber ninguna forma portátil de manejar tal señal.

En la práctica, nunca escuché de una implementación que aproveche la nueva redacción en C99. Para todos los compiladores que he escuchado, el resultado de la conversión está definido por la implementación, y muy probablemente rinda lo que esperaría de un truncamiento de complemento a 2. (Y no estoy del todo convencido de que este cambio en C99 sea una buena idea. Si nada más, hizo que esta respuesta sea aproximadamente 3 veces más larga de lo que hubiera necesitado).