Conversión de tipo-unsigned a signed int / char


He intentado ejecutar el siguiente programa:

#include <stdio.h>

int main() {
    signed char a = -5;
    unsigned char b = -5;
    int c = -5;
    unsigned int d = -5;

    if (a == b)
        printf("\r\n char is SAME!!!");
    else
        printf("\r\n char is DIFF!!!");

    if (c == d)
        printf("\r\n int is SAME!!!");
    else
        printf("\r\n int is DIFF!!!");

    return 0;
}

Para este programa, estoy obteniendo la salida:

Char es DIFF!!! int es IGUAL!!!

¿Por qué estamos obteniendo salidas diferentes para ambos?
¿Debe la salida ser como abajo ?

Char es LO MISMO!!! int es IGUAL!!!

A codepad link.

Author: legends2k, 2013-06-26

5 answers

Esto se debe a las varias reglas implícitas de conversión de tipos en C. Hay dos de ellas que un programador de C debe conocer: las conversiones aritméticas habituales y las promociones de enteros (las últimas son parte de las primeras).

En el caso char tenemos los tipos (signed char) == (unsigned char). Estos son tipos enteros pequeños. Otros tipos de enteros pequeños son bool y short. Las reglas de promoción de enteros establecen que cada vez que un tipo entero pequeño es un operando de una operación, su tipo será promovido a int, que está firmado. Esto sucederá sin importar si el tipo estaba firmado o sin firmar.

En el caso del signed char, el signo se conservará y se promocionará a un int que contenga el valor -5. En el caso del unsigned char, contiene un valor que es 251 (0xFB ). Se promocionará a un int que contenga ese mismo valor. Usted termina con

if( (int)-5 == (int)251 )

En el caso entero tienes los tipos (signed int) == (unsigned int). No son tipos enteros pequeños, por lo que las promociones de enteros no se aplican. En su lugar, están equilibrados por las conversiones aritméticas habituales, que establecen que si dos operandos tienen el mismo "rango" (tamaño) pero diferente signedness, el operando firmado se convierte al mismo tipo que el sin signo. Usted termina con

if( (unsigned int)-5 == (unsigned int)-5)
 79
Author: Lundin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-07 07:52:12

¡Buena pregunta!

La comparación int funciona, porque ambos int contienen exactamente los mismos bits, por lo que son esencialmente los mismos. Pero, ¿qué pasa con los chars?

Ah, C implícitamente promueve chars a int s en varias ocasiones. Este es uno de ellos. Su código dice if(a==b), pero lo que el compilador en realidad se convierte en:

if((int)a==(int)b) 

(int)a es -5, pero (int)b es 251. Definitivamente no son lo mismo.

EDITAR: Como @ Carbonic-Acid señaló, (int)b es 251 solamente si a char es de 8 bits de largo. Si int tiene 32 bits de largo, (int)b es -32764.

REDIT: Hay un montón de comentarios discutiendo la naturaleza de la respuesta si un byte no tiene 8 bits de largo. La única diferencia en este caso es que (int)b no es 251 sino un número positivo diferente, que no es -5. Esto no es realmente relevante para la pregunta que sigue siendo muy cool.

 36
Author: zmbq,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-30 21:05:53

Bienvenido a promoción de enteros. Si me permiten citar del sitio web:

Si un int puede representar todos los valores del tipo original, el valor es convertido a un int; de lo contrario, se convierte a un int sin signo. Estos se llaman las promociones de enteros. Todos los demás tipos no cambian por las promociones enteras.

C puede ser muy confuso cuando haces comparaciones como estas, recientemente desconcerté a algunos de mis amigos de programación no C con lo siguiente tease:

#include <stdio.h>
#include <string.h>

int main()
{
    char* string = "One looooooooooong string";

    printf("%d\n", strlen(string));

    if (strlen(string) < -1) printf("This cannot be happening :(");

    return 0;
}

Que de hecho imprime This cannot be happening :( y aparentemente demuestra que 25 es más pequeño que -1!

Lo que sucede debajo, sin embargo, es que -1 se representa como un entero sin signo que, debido a la representación de bits subyacente, es igual a 4294967295 en un sistema de 32 bits. Y naturalmente 25 es menor que 4294967295.

Si, sin embargo, lanzamos explícitamente el tipo size_t devuelto por strlen como un entero con signo:

if ((int)(strlen(string)) < -1)

Entonces comparará 25 contra -1 y todos estará bien con el mundo.

Un buen compilador debería advertirle sobre la comparación entre un entero sin signo y un entero con signo y, sin embargo, todavía es muy fácil pasar por alto (especialmente si no habilita las advertencias).

Esto es especialmente confuso para los programadores Java ya que todos los tipos primitivos están firmados. Esto es lo que James Gosling (uno de los creadores de Java) tenía que decir sobre el tema:

Gosling: Para mí como diseñador de lenguaje, que realmente no cuento yo mismo como en estos días, lo que" simple " realmente terminó significando era Espero que J. Random Developer mantenga la especificación en su cabeza. Que definición dice que, por ejemplo, Java no es Java y de hecho un montón de estos idiomas terminan con un montón de casos de esquina, cosas que nadie realmente entiende. Quiz cualquier desarrollador de C sobre sin firmar, y bastante pronto descubrirás que casi ningún desarrollador de C realmente entiende lo que continúa con sin signo, lo que es aritmética sin signo. Cosas como esa hecho C complejo. La parte del lenguaje de Java es, creo, bastante simple. Las bibliotecas que tienes que buscar.

 20
Author: Nobilis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-03 08:59:30

La representación hexadecimal de -5 es:

  • 8 bits, complemento de dos signed char: 0xfb
  • 32 bits, complemento de dos signed int: 0xfffffffb

Cuando se convierte un número con signo en un número sin signo, o viceversa, el compilador lo hace ... precisamente nada. ¿Qué hay que hacer? El número es convertible o no lo es, en cuyo caso sigue un comportamiento indefinido o definido por la implementación (en realidad no he comprobado cuál) y el más eficiente definido por la implementación el comportamiento es no hacer nada.

Entonces, la representación hexadecimal de (unsigned <type>)-5 es:

  • 8 bits, unsigned char: 0xfb
  • 32-bit, unsigned int: 0xfffffffb

¿Te resulta familiar? Son poco a poco lo mismo que las versiones firmadas.

Cuando escribes if (a == b), donde a y b son de tipo char, lo que el compilador realmente debe leer es if ((int)a == (int)b). (Esta es la "promoción de enteros" sobre la que todos los demás están golpeando.)

Entonces, ¿qué sucede cuando convertir char a int?

  • de 8 bits signed char a 32 bits signed int: 0xfb -> 0xfffffffb
    • Bueno, eso tiene sentido porque coincide con las representaciones de -5 arriba!
    • Se llama "sign-extend", porque copia el bit superior del byte, el "sign-bit", hacia la izquierda en el nuevo valor más amplio.
  • 8 bits unsigned char a 32 bits signed int: 0xfb -> 0x000000fb
    • Esta vez hace una "extensión cero" porque el tipo de fuente es sin signo , por lo que hay no hay señal-bit para copiar.

Entonces, a == b realmente 0xfffffffb == 0x000000fb = > ¡no hay coincidencia!

Y c == d realmente 0xfffffffb == 0xfffffffb => partido!

 9
Author: ams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-04 12:48:38

Mi punto es: ¿no recibió una advertencia en tiempo de compilación "comparando expresiones firmadas y no firmadas"?

El compilador está tratando de informarle que tiene derecho a hacer cosas locas! :) Yo añadiría, cosas locas sucederán usando grandes valores, cerca de la capacidad del tipo primitivo. Y

 unsigned int d = -5;

Es asignar definitivamente un valor grande a d, es equivalente (incluso si, probablemente no se garantiza que sea equivalente) a ser:

 unsigned int d = UINT_MAX -4; ///Since -1 is UINT_MAX

Editar:

Sin Embargo, es es interesante notar que solo la segunda comparación da una advertencia (verifique el código). Por lo tanto, significa que el compilador que aplica las reglas de conversión confía en que no habrá errores en la comparación entre unsigned char y char (durante la comparación se convertirán a un tipo que pueda representar con seguridad todos sus valores posibles). Y tiene razón en este punto. Entonces, le informa que este no será el caso para unsigned int y int: durante la comparación uno de los 2 será convertido a un tipo que no puede representarlo completamente.

Para completar, Lo revisé también para short: el compilador se comporta de la misma manera que para los caracteres, y, como se esperaba, no hay errores en tiempo de ejecución.

.

Relacionado con este tema, hace poco hice esta pregunta (sin embargo, orientado a C++).

 1
Author: Antonio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:31:55