¿Qué sucede si asigno un valor negativo a una variable sin signo?

81

Tenía curiosidad por saber qué pasaría si le asigno un valor negativo a una variable sin firmar.

El código se parecerá a esto.

unsigned int nVal = 0;
nVal = -5;

No me dio ningún error de compilación. ¡Cuando ejecuté el programa, nValse le asignó un valor extraño! ¿Podría ser que se le asigne algún valor de complemento a 2 nVal?

ckv
fuente
1
Mi corazonada (aún no he podido encontrarlo en el estándar) es que el comportamiento no está definido técnicamente. Además, sospecho que verá lo que espera en prácticamente cualquier compilador que pueda encontrar. Entonces, aunque normalmente verá ese comportamiento, probablemente no sea una buena idea contar con él.
sblom
6
No está indefinido (ver §4.7 / 2 ), pero la representación (por ejemplo, complemento a 2) no es obligatoria por el estándar.
Georg Fritzsche
@gf (et al. a continuación), genial. Parece que el comportamiento está, de hecho, definido explícitamente como lo que esperabas, @viswanathan.
sblom
3
La segunda línea es equivalente a nVal = (unsigned int) -5;. El reparto de -5to unsigned intse define en 6.3.1.3. La representación en complemento a 2 no es obligatoria por el estándar, pero el algoritmo para convertir a unsigned es: "el valor se convierte agregando o restando repetidamente uno más del valor máximo que se puede representar en el nuevo tipo hasta que el valor esté en el rango del nuevo tipo ".
Pascal Cuoq
1
@Pascal: Parece que se está refiriendo a C99, pero la pregunta está etiquetada como C ++.
Georg Fritzsche

Respuestas:

67

Para la respuesta oficial - Sección 4.7 conv.integral

"Si el tipo de destino no tiene signo, el valor resultante es el menor entero sin signo congruente con el entero de origen (módulo 2 n donde nes el número de bits utilizados para representar el tipo sin signo). [Nota: En una representación de complemento a dos, esta conversión es conceptual y no hay cambios en el patrón de bits (si no hay truncamiento). —nota final]

Básicamente, esto significa que si la arquitectura subyacente se almacena en un método que no es Complemento de dos (como Magnitud con signo o Complemento de uno), la conversión a unsigned debe comportarse como si fuera Complemento de dos.

Dennis Zickefoose
fuente
37
¿Qué significa el menor entero sin signo congruente con el entero fuente ?
David Rodríguez - dribeas
11
@ DavidRodríguez-dribeas Como ejemplo, 5 y 3 son "mod 2 congruentes" ya que 5% 2 y 3% 2 son ambos 1.
JoeQuery
¿Con qué versiones del estándar C ++ se relaciona? ¿Todas?
Alexey Kruglov
36

Asignará el patrón de bits que representa -5 (en complemento a 2) al int sin signo. Lo cual será un gran valor sin firmar. Para entradas de 32 bits, será 2 ^ 32 - 5 o 4294967291

Jasmeet
fuente
2
Los bits no tienen nada que ver con eso.
GManNickG
1
@BenVoigt: Muy bien, quise decir que no tiene nada que ver con cómo se interpretan los bits. (Es decir, los "bits" en la parte citada son una abreviatura de ceil(log_2(x))).
GManNickG
1
@GManNickG Bit's (como en, pertenece al bit)? ¿El cumplido de 2 (eso es muy amable de su parte) ? GAAAAAAAAAAAAAH!
NullUserException
1
@NullUserException: Jaja, lo sé. Escribir "* 's" en lugar de solo "* s" es un hábito terrible que he tenido durante un tiempo. En cuanto al cumplido en lugar del complemento, eso es pura tontería. :)
GManNickG
La simplicidad es clave. Esta respuesta tiene eso. (2 ^ 32 - 5) explica este comportamiento mejor que citar la documentación.
Redhart
4

Se mostrará como un entero positivo de valor de máximo entero sin signo - 4 (el valor depende de la arquitectura de la computadora y el compilador).

Por cierto,
puede verificar esto escribiendo un programa simple de tipo "hola mundo" en C ++ y verlo usted mismo

Ayudante de Dror
fuente
Lo escribí y lo verifiqué, por eso hice la pregunta, pero no sabía cómo llegó el compilador a ese valor positivo. Gracias
ckv
6
Desafortunadamente, con C ++, escribir programas para probar el comportamiento no siempre es una buena idea. Por ejemplo, si uno intenta probar lo que sucede en el caso de un desbordamiento firmado , conducirá a un comportamiento indefinido, que no se garantiza que sea el mismo en todas las máquinas / compiladores.
Ben Jones
4

Tiene razón, el entero con signo se almacena en forma de complemento a 2 y el entero sin signo se almacena en la representación binaria sin signo . C (y C ++) no distingue entre los dos, por lo que el valor con el que termina es simplemente el valor binario sin signo de la representación binaria del complemento a 2.

perimosocordiae
fuente
16
Puede que no se guarde en el complemento de 2.
GManNickG
¿Qué significa si algo está "almacenado en 2"? @GManNickG
JeremyF
4
@JeremyF: No "2's", "2's cumplido". Es un término compatible con Google y una forma de representar números enteros con signo.
GManNickG
2

Sí, tienes razón. El valor real asignado es algo así como todos los bits establecidos excepto el tercero. -1 son todos los bits establecidos (hexadecimal: 0xFFFFFFFF), -2 son todos los bits excepto el primero y así sucesivamente. Lo que vería probablemente sea el valor hexadecimal 0xFFFFFFFB que en decimal corresponde a 4294967291.

Martín
fuente
3
Los bits no tienen nada que ver con eso, la representación de números enteros no se especifica.
GManNickG
2
Tu respuesta es correcta, estricta, va al grano y es algo que nunca usaría en clase.
Martin
vea mi respuesta para el complemento a 2 de -5. No creo que hayas hecho correctamente tus cálculos con los valores binarios aquí.
cynistersix
0

Cuando asigna un valor negativo a una variable sin signo, utiliza el método de complemento a 2 para procesarla y, en este método, cambia todos los 0 a 1 y todos los 1 a 0 y luego le agrega 1. En su caso, está tratando con int, que es de 4 bytes (32 bits), por lo que intenta usar el método de complemento a 2 en un número de 32 bits, lo que hace que el bit superior se invierta. Por ejemplo:

┌─[student@pc]─[~]
└──╼ $pcalc 0y00000000000000000000000000000101      # 5 in binary
        5                       0x5                     0y101
┌─[student@pc]─[~]
└──╼ $pcalc 0y11111111111111111111111111111010      # flip all bits  
      4294967290      0xfffffffa      0y11111111111111111111111111111010
┌─[student@pc]─[~]
└──╼ $pcalc 0y11111111111111111111111111111010 + 1  # add 1 to that flipped binarry
      4294967291      0xfffffffb      0y11111111111111111111111111111011
Zafeer
fuente