¿Esto invoca un comportamiento indefinido?

5 minutos de lectura

¿Esto invoca un comportamiento indefinido
Quijotesco

Considere el siguiente programa en C:

#include <stdio.h>

int main(){
    int a =-1;
    unsigned b=-1;
    if(a==b)
        printf("%d %d",a,b);
    else
       printf("Unequal");
    return 0;
 }

En la linea printf("%d %d",a,b);, "%d" se utiliza para imprimir un tipo sin firmar. ¿Invoca esto un comportamiento indefinido y por qué?

¿Esto invoca un comportamiento indefinido
CB Bailey

Aunque se le permite explícitamente utilizar el va_arg macro de <stdarg.h> para recuperar un parámetro que se pasó como unsigned como un int (7.15.1.1/2), en la documentación para fprintf (7.19.6.1/9) que también se aplica a printfestablece explícitamente que si algún argumento no es del tipo correcto para el especificador de formato – para un no modificado %des decir int – entonces el comportamiento no está definido.

Como señala @bdonlan en un comentario, si el valor de b (en este caso 2^N - 1 para algunos N) no es representable en un int entonces seria comportamiento indefinido para intentar acceder al valor como un int utilizando va_arg En todo caso. Esto solo funcionaría en plataformas donde la representación de un unsigned usó al menos un bit de relleno donde el correspondiente int representación tenía un bit de signo.

Incluso en el caso de que el valor de (unsigned)-1 puede representarse en un inttodavía leo esto como técnicamente comportamiento indefinido. Como parte de la implementación, parecería estar permitido que una implementación use magia incorporada en lugar de va_args para acceder a los parámetros de printf y si pasas algo como un unsigned donde un int es necesario, entonces usted ha violado técnicamente el contrato por printf.

  • La excepción en 7.15.1.1/2 dice lo siguiente: “si el tipo no es compatible con el tipo del siguiente argumento real […]el comportamiento no está definido, excepto [where] un tipo es un tipo entero con signo, el otro tipo es el tipo entero sin signo correspondiente, y el valor es representable en ambos tipos(énfasis mío). Desde -1 no es representable en ambos tipos, el comportamiento no está definido incluso sin 7.19.6.1/9

    – bdonlan

    1 de mayo de 2011 a las 21:42


  • @bdonlan: Técnicamente b no tiene el valor -1tiene el valor 2^N-1 para algunos N. Si este valor es representable tanto en un int y un unsigned depende de la plataforma, por lo general no, te lo concedo.

    –CB Bailey

    1 mayo 2011 a las 21:45


  • Si un comportamiento no está definido solo si algún factor definido por la implementación es verdadero, entonces está efectivamente indefinido, ya que una implementación conforme es libre de elegir un valor de INT_MAX y UINT_MAX que le permitirá invocar demonios nasales en la llamada de printf en cuestión.

    – bdonlan

    1 de mayo de 2011 a las 21:48

  • @bdonlan: no es “efectivamente indefinido” porque la implementación tiene que documentar INT_MAX y UINT_MAX` por lo que es posible determinar si no es UB para su implementación particular. ¿A menos que quisieras decir algo más con “efectivamente indefinido”? No es universalmente portátil en cualquier caso.

    –CB Bailey

    1 mayo 2011 a las 21:55

  • Lo que quiero decir es que, por lo general, un programa no puede asumir ninguna semántica específica para él. Claro, puedes hacer un #if INT_MAX >= (UINT_MAX - 1)pero, en la práctica, también podría asumir que siempre es UB y terminar con eso

    – bdonlan

    1 de mayo de 2011 a las 22:20

1646961907 241 ¿Esto invoca un comportamiento indefinido
ataúd de jerry

La norma no es 100% clara en este punto. Por un lado, obtienes la especificación para va_argque dice (§7.15.1.1/2):

Si no hay un siguiente argumento real, o si el tipo no es compatible con el tipo del siguiente argumento real (como se promociona de acuerdo con las promociones de argumento predeterminadas), el comportamiento no está definido, excepto en los siguientes casos:

  • un tipo es un tipo entero con signo, el otro tipo es el tipo entero sin signo correspondiente y el valor se puede representar en ambos tipos;
  • un tipo es un puntero a void y el otro es un puntero a un tipo de carácter.

Por otro lado, obtienes la especificación de printf (§7.19.6.1/9):

Si algún argumento no es del tipo correcto para la especificación de conversión correspondiente, el comportamiento no está definido”.

Dado que es más o menos un hecho que printf recuperará argumentos con va_arg, diría que está bastante seguro con valores que se pueden representar en el tipo de destino, pero no de otra manera. Dado que convirtió -1 en un sin signo antes de pasarlo, el valor estará fuera del rango que se puede representar en un int con signo, por lo que el comportamiento no estará definido.

  • Excepto que (sin firmar)-1 se convierte en (UINT_MAX – 1), que generalmente está más allá del rango de INT_MAX…

    – bdonlan

    1 mayo 2011 a las 21:50

  • Me pregunto si hay implementaciones donde el uso de %X y amigos con valores firmados, ¿hace algo más que mostrar la representación hexadecimal de los bits subyacentes? De lo contrario, parecería un comportamiento útil que debería incluirse en el estándar, especialmente porque gran parte del código existente se basa en ese comportamiento.

    – Super gato

    16 dic 2015 a las 18:36

Sí el if siempre se evaluará como verdadero y el printf intentará imprimir un unsigned como un signed. Desde el signed El tipo puede tener representaciones trampa, esto puede ser UB si la representación del signo es el complemento de uno.

¿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