Respuesta corta
Su i
se convertirá en un entero sin signo agregando UINT_MAX + 1
, luego la adición se llevará a cabo con los valores sin signo, dando como resultado un gran result
(dependiendo de los valores de u
y i
).
Respuesta larga
De acuerdo con el estándar C99:
6.3.1.8 Conversiones aritméticas usuales
- Si ambos operandos tienen el mismo tipo, no se necesita más conversión.
- De lo contrario, si ambos operandos tienen tipos enteros con signo o ambos tienen tipos enteros sin signo, el operando con el tipo de rango de conversión de entero menor se convierte al tipo del operando con rango mayor.
- De lo contrario, si el operando que tiene un tipo entero sin signo tiene un rango mayor o igual al rango del tipo del otro operando, entonces el operando con tipo entero con signo se convierte en el tipo del operando con un tipo entero sin signo.
- De lo contrario, si el tipo de operando con tipo entero con signo puede representar todos los valores del tipo de operando con tipo entero sin signo, entonces el operando con tipo entero sin signo se convierte en el tipo de operando con tipo entero con signo.
- De lo contrario, ambos operandos se convierten al tipo entero sin signo correspondiente al tipo del operando con tipo entero con signo.
En su caso, tenemos uno sin signo int ( u
) y firmado int ( i
). En referencia a (3) anterior, dado que ambos operandos tienen el mismo rango, i
será necesario convertirlo a un entero sin signo.
6.3.1.3 Enteros con y sin signo
- Cuando un valor con tipo entero se convierte en otro tipo entero distinto de _Bool, si el valor puede ser representado por el nuevo tipo, no se modifica.
- De lo contrario, si el nuevo tipo no tiene signo, el valor se convierte agregando o restando repetidamente uno más que el valor máximo que se puede representar en el nuevo tipo hasta que el valor esté en el rango del nuevo tipo.
- De lo contrario, el nuevo tipo se firma y el valor no se puede representar en él; el resultado está definido por la implementación o se genera una señal definida por la implementación.
Ahora necesitamos referirnos a (2) arriba. Su i
se convertirá en un valor sin signo al agregar UINT_MAX + 1
. Por lo tanto, el resultado dependerá de cómo UINT_MAX
se defina en su implementación. Será grande, pero no se desbordará porque:
6.2.5 (9)
Un cálculo que involucra operandos sin signo nunca puede desbordarse, porque un resultado que no puede ser representado por el tipo entero resultante sin signo se reduce módulo el número que es uno mayor que el valor más grande que puede ser representado por el tipo resultante.
Bonus: Conversión Aritmética Semi-WTF
#include <stdio.h>
int main(void)
{
unsigned int plus_one = 1;
int minus_one = -1;
if(plus_one < minus_one)
printf("1 < -1");
else
printf("boring");
return 0;
}
Puede usar este enlace para probar esto en línea: https://repl.it/repls/QuickWhimsicalBytes
Bonus: efecto secundario de conversión aritmética
Las reglas de conversión aritmética se pueden usar para obtener el valor UINT_MAX
inicializando un valor sin signo -1
, es decir:
unsigned int umax = -1; // umax set to UINT_MAX
Se garantiza que es portátil independientemente de la representación de números firmados del sistema debido a las reglas de conversión descritas anteriormente. Consulte esta pregunta SO para obtener más información: ¿Es seguro usar -1 para establecer todos los bits en verdadero?
La conversión de firmado a no firmado no solo copia o reinterpreta necesariamente la representación del valor firmado. Citando el estándar C (C99 6.3.1.3):
Para la representación del complemento de los dos que es casi universal en estos días, las reglas corresponden a la reinterpretación de los bits. Pero para otras representaciones (signo y magnitud o complemento de unos), la implementación de C aún debe organizar el mismo resultado, lo que significa que la conversión no puede simplemente copiar los bits. Por ejemplo, (sin firmar) -1 == UINT_MAX, independientemente de la representación.
En general, las conversiones en C se definen para operar en valores, no en representaciones.
Para responder la pregunta original:
El valor de i se convierte en unsigned int, rindiendo
UINT_MAX + 1 - 5678
. Este valor se agrega luego al valor sin signo 1234, dando como resultadoUINT_MAX + 1 - 4444
.(A diferencia del desbordamiento no firmado, el desbordamiento firmado invoca un comportamiento indefinido. Wraparound es común, pero no está garantizado por el estándar C, y las optimizaciones del compilador pueden causar estragos en el código que hace suposiciones injustificadas).
fuente
Refiriéndose a la biblia :
fuente
Cuando se agregan una variable sin signo y una con signo (o cualquier operación binaria) ambas se convierten implícitamente en sin signo, lo que en este caso daría como resultado un gran resultado.
Por lo tanto, es seguro en el sentido de que el resultado puede ser enorme e incorrecto, pero nunca se bloqueará.
fuente
Al convertir de firmado a no firmado hay dos posibilidades. Los números que originalmente eran positivos permanecen (o se interpretan como) el mismo valor. Los números que originalmente eran negativos ahora se interpretarán como números positivos más grandes.
fuente
Como se respondió anteriormente, puede emitir de un lado a otro entre firmado y sin firmar sin ningún problema. El caso límite para los enteros con signo es -1 (0xFFFFFFFF). Intenta sumar y restar de eso y verás que puedes volver a lanzar y hacer que sea correcto.
Sin embargo, si va a emitir de un lado a otro, le recomiendo nombrar sus variables de modo que quede claro de qué tipo son, por ejemplo:
Es demasiado fácil distraerse con problemas más importantes y olvidar qué variable es de qué tipo si se nombran sin una pista. No desea convertir a un sin firmar y luego usarlo como un índice de matriz.
fuente
Seré convertido a un entero sin signo.
Seguro en el sentido de estar bien definido sí (consulte https://stackoverflow.com/a/50632/5083516 ).
Las reglas están escritas en lenguaje estándar difícil de leer, pero esencialmente cualquier representación que se haya utilizado en el entero con signo, el entero sin signo contendrá una representación complementaria de 2 del número.
La suma, la resta y la multiplicación funcionarán correctamente en estos números, dando como resultado otro número entero sin signo que contiene un número de complemento a dos que representa el "resultado real".
la división y la conversión a tipos enteros sin signo más grandes tendrán resultados bien definidos, pero esos resultados no serán representaciones complementarias de 2 del "resultado real".
Mientras que las conversiones de firmado a sin signo están definidas por el estándar, el reverso está definido por la implementación, tanto gcc como msvc definen la conversión de manera que obtendrá el "resultado real" al convertir el número de complemento de 2 almacenado en un entero sin signo de nuevo a un entero con signo . Espero que solo encuentre cualquier otro comportamiento en sistemas oscuros que no usan el complemento de 2 para enteros con signo.
https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation https://msdn.microsoft.com/en-us/library/0eex498h.aspx
fuente
Respuestas horribles en abundancia
Ozgur Ozcitak
Esto está completamente mal.
Mats Fredriksson
Esto también está mal. Los ints sin signo pueden promoverse a ints si tienen la misma precisión debido a los bits de relleno en el tipo sin signo.
smh
Incorrecto. Tal vez sí y tal vez no.
Incorrecto. Es un comportamiento indefinido si causa un desbordamiento o si se conserva el valor.
Anónimo
Incorrecto. Depende de la precisión de un int relativo a un int sin signo.
Taylor Price
Incorrecto. Intentar almacenar un valor fuera del rango de un entero con signo da como resultado un comportamiento indefinido.
Ahora finalmente puedo responder la pregunta.
Si la precisión de int es igual a unsigned int, u será promovido a int firmado y obtendrá el valor -4444 de la expresión (u + i). Ahora, si tu y yo tenemos otros valores, puedes obtener un desbordamiento y un comportamiento indefinido, pero con esos números exactos obtendrás -4444 [1] . Este valor tendrá el tipo int. Pero está tratando de almacenar ese valor en un int sin signo para que luego se convierta en un int sin signo y el valor que terminará teniendo sería (UINT_MAX + 1) - 4444.
En caso de que la precisión de unsigned int sea mayor que la de int, se promoverá a unsigned int a unsigned int produciendo el valor (UINT_MAX + 1) - 5678 que se agregará a la otra unsigned int 1234. Si tu y yo tenemos otros valores, que hacen que la expresión caiga fuera del rango {0..UINT_MAX}, el valor (UINT_MAX + 1) se agregará o restará hasta que el resultado NO se encuentre dentro del rango {0..UINT_MAX) y no se producirá un comportamiento indefinido .
¿Qué es la precisión?
Los enteros tienen bits de relleno, bits de signo y bits de valor. Los enteros sin signo no tienen un bit de signo obviamente. Unsigned char está garantizado además de no tener bits de relleno. El número de bits de valores que tiene un entero es la precisión que tiene.
[Gotchas]
El tamaño de macro de la macro por sí solo no se puede utilizar para determinar la precisión de un entero si hay bits de relleno. Y el tamaño de un byte no tiene que ser un octeto (ocho bits) como lo define C99.
[1] El desbordamiento puede ocurrir en uno de dos puntos. Antes de la adición (durante la promoción): cuando tiene un int sin firmar que es demasiado grande para caber dentro de un int. El desbordamiento también puede ocurrir después de la adición, incluso si el int sin signo estaba dentro del rango de un int, después de la adición, el resultado puede desbordarse.
fuente
int
se convierteunsigned int
cuando se aplican las conversiones aritméticas habituales.int
ounsigned int
a uno de esos tipos que algo del tipounsigned int
oint
se espera. El "o igual" se agregó en TC2 para permitir que los tipos enumerados de rango de conversión sean igualesint
ounsigned int
se conviertan a uno de esos tipos. Nunca se pretendió que la promoción descrita se convirtiera entreunsigned int
yint
. La determinación de tipo común entreunsigned int
yint
todavía se rige por 6.3.1.8, incluso después de TC2.