c - positivos - restas negativas para niños
¿Asignar números negativos a un int sin firmar? (14)
En el lenguaje de programación C, unsigned int
se utiliza para almacenar valores positivos solamente. Sin embargo, cuando ejecuto el siguiente código:
unsigned int x = -12;
printf("%d", x);
La salida sigue siendo -12. Pensé que debería haber impreso: 12, ¿o estoy mal entendiendo algo?
Cuando el compilador convierte implícitamente -12 en un entero sin signo, la representación binaria subyacente permanece inalterada. Esta conversión es puramente semántica. El bit de signo del entero del complemento de dos se convierte en el bit más significativo del entero sin signo. Por lo tanto, cuando printf trata el entero sin signo como un entero con signo con% d, verá -12.
Cuando intenta mostrar el valor de int, lo pasa a un argumento (int) y no a un argumento (sin signo) y eso hace que se imprima -12 y no 4294967284. Los enteros se almacenan en formato hexadecimal y -12 para int es lo mismo que 4294967284 para int sin signo en formato hexadecimal. Por eso "% u" imprime el valor correcto que desea y no "% d". Depende de su tipo de argumento ... ¡BUENA SUERTE!
El -12 está en formato de complemento de 16 bits 2. Entonces haz esto: si (x & 0x8000) {x = ~ x + 1; } Esto convertirá el número -ve complementario de 2 en el número equivalente + ve. Buena suerte.
El -12
a la derecha de su signo igual se configura como un entero con signo (probablemente de 32 bits de tamaño) y tendrá el valor hexadecimal 0xFFFFFFF4
. El compilador genera código para mover este entero con signo a su entero sin signo x
que también es una entidad de 32 bits. El compilador asume que solo tiene un valor positivo a la derecha del signo igual por lo que simplemente mueve todos los 32 bits a x
. x
ahora tiene el valor 0xFFFFFFF4
que es 4294967284
si se interpreta como un número positivo. Pero el formato printf
de %d
dice que los 32 bits deben interpretarse como un entero con signo para que obtenga -12
. Si hubiera utilizado %u
, se habría impreso como 4294967284
.
En cualquier caso, no obtienes lo que esperabas, ya que el lenguaje C "confía" en el autor del código para que solo pida cosas "sensatas". Esto es común en C. Si quisiera asignar un valor a x
y no estaba seguro de si el valor en el lado derecho de los iguales era positivo, podría haber escrito unsigned int x = abs(-12);
y forzó al compilador a generar código para tomar el valor absoluto de un entero con signo antes de moverlo al entero sin signo.
El int es unsinged, pero le ha dicho a printf
que lo vea como un int firmado.
Tratar
unsigned int x = -12; printf("%u", x);
No imprimirá "12", pero imprimirá el valor máximo de un int sin signo menos 11.
El ejercicio para el lector es descubrir por qué :)
Ellos sí almacenan valores positivos. Pero está generando el valor positivo (muy alto) como un entero con signo, por lo que puede ser reinterpretado nuevamente (en una forma definida por la implementación, podría agregar).
Utilice la marca de formato "%u
lugar.
En el contexto general, cuando solo se pueden almacenar números positivos, los números negativos no se almacenan explícitamente, sino que se almacenan los 2 complementos. De la misma manera aquí, el complemento de 2 de -12 se almacenará en ''x'' y usará %u
para obtenerlo.
La asignación de un valor negativo a un int sin signo no calcula el valor absoluto del negativo: interpreta como un int sin signo la representación binaria del valor negativo, es decir, 4294967284 (2 ^ 32 - 12).
printf ("% d") realiza la interpretación opuesta. Es por eso que su programa muestra -12.
Lo que te falta es que el printf ("% d", x) espera que x esté firmado, por lo que aunque asignas -12 a x, se interpreta como un complemento de 2 que sería un número muy grande. Sin embargo, cuando pasa este número realmente grande a printf, lo interpreta como firmado, por lo que se traduce correctamente a -12.
La sintaxis correcta para imprimir un unsigned en print f es "% u". ¡Intente esto y vea lo que hace!
Pasar% d a printf le dice a printf que trate el argumento como un entero con signo, independientemente de lo que realmente pase. Utilice% u para imprimir como sin firmar.
Todo tiene que ver con la interpretación del valor.
Si asume enteros de 16 bits con signo y sin signo, aquí hay algunos ejemplos que no son exactamente correctos, pero demuestran el concepto.
0000 0000 0000 1100 int sin signo y valor int firmado 12
1000 0000 0000 1100 valor int firmado -12 y un entero sin signo grande.
Para enteros con signo, el bit de la izquierda es el bit de signo. 0 = positivo 1 = negativo
Para enteros sin signo, no hay bit de signo. el bit de la mano izquierda, le permite almacenar un número mayor en su lugar.
Así que la razón por la que no estás viendo lo que estás esperando es esa.
unsigned int x = -12, toma -12 como un entero, y lo almacena en x. x no está firmado, así que lo que era un bit de signo, ahora es una parte del valor.
printf le permite decirle al compilador cómo desea que se muestre un valor.
% d significa mostrarlo como si fuera un int firmado. % u significa mostrarlo como si fuera un int sin firmar.
c te permite hacer este tipo de cosas. Usted el programador está en control.
Algo así como un arma de fuego. Es una herramienta. Puede usarlo correctamente para lidiar con ciertas situaciones, o incorrectamente para quitar uno de sus dedos.
Un posible caso útil es el siguiente
unsigned int allBitsOn = -1;
Ese valor particular pone todos los bits a 1
1111 1111 1111 1111
Eso puede ser útil a veces.
Tu programa tiene un comportamiento indefinido porque pasaste el tipo incorrecto a printf
(le dijiste que ibas a pasar un int
pero que pasaste un unsigned int
). Considera afortunado que lo "más fácil" que hizo la implementación fue simplemente imprimir en silencio el valor incorrecto y no saltar a un código que hace algo dañino ...
int y unsigned int se utilizan para asignar un número de bytes para almacenar un valor nada más.
El compilador debe advertir sobre la falta de coincidencia de la firma, pero en realidad no afecta a los bits en la memoria que representan el valor -12.
% x,% d,% u, etc. le dice al compilador cómo interrumpir una cantidad de bits cuando los imprime.
printf(''%d'', x);
Significa imprimir un entero con signo. Tendrás que escribir esto en su lugar:
printf(''%u'', x);
Además, todavía no imprimirá "12", será "4294967284".