Guantánamo
Ver este fragmento de código
int main()
{
unsigned int a = 1000;
int b = -1;
if (a>b) printf("A is BIG! %d\n", a-b);
else printf("a is SMALL! %d\n", a-b);
return 0;
}
Esto da la salida: a es PEQUEÑO: 1001
No entiendo lo que está pasando aquí. ¿Cómo funciona aquí el operador >? ¿Por qué “a” es más pequeña que “b”? Si es más pequeño, ¿por qué obtengo un número positivo (1001) como diferencia?
Hormiga
Las operaciones binarias entre diferentes tipos integrales se realizan dentro de un tipo “común” definido por los llamados conversiones aritméticas habituales (ver la especificación de idioma, 6.3.1.8). En su caso, el tipo “común” es unsigned int
. Esto significa que int
operando (su b
) se convertirá en unsigned int
antes de la comparación, así como con el fin de realizar la resta.
Cuando -1
se convierte en unsigned int
el resultado es el máximo posible unsigned int
valor (igual que UINT_MAX
). No hace falta decir que va a ser mayor que su no firmado 1000
valor, lo que significa que a > b
de hecho es falso y a
es de hecho pequeña comparado con (unsigned) b
. Él if
en su código debe resolver a else
rama, que es lo que observaste en tu experimento.
Las mismas reglas de conversión se aplican a la resta. Su a-b
se interpreta realmente como a - (unsigned) b
y el resultado tiene tipo unsigned int
. Dicho valor no se puede imprimir con %d
especificador de formato, ya que %d
solo funciona con firmado valores. Su intento de imprimirlo con %d
da como resultado un comportamiento indefinido, por lo que el valor que ve impreso (aunque tiene una explicación lógica determinista en la práctica) no tiene ningún sentido desde el punto de vista del lenguaje C.
Editar: En realidad, podría estar equivocado sobre la parte del comportamiento indefinido. De acuerdo con la especificación del lenguaje C, la parte común del rango del tipo de entero con signo y sin signo correspondiente debe tener una representación idéntica (lo que implica, según la nota al pie 31, “intercambiabilidad como argumentos para funciones”). Entonces, el resultado de a - b
la expresión no está firmada 1001
como se describe arriba, y a menos que me falte algo, es legal imprimir este valor específico sin firmar con %d
especificador, ya que cae dentro del rango positivo de int
. Impresión (unsigned) INT_MAX + 1
con %d
sería indefinido, pero 1001u
está bien.
-
Aunque sabemos o podemos adivinar lo suficiente sobre la convención de llamada de su implementación para concluir que lo que sucedió es el resultado sin signo de ab, que de hecho es 1001, pasó a través de los varargs ileso y se reinterpretó como firmado sin cambiar el valor.
–Steve Jessop
18 de enero de 2010 a las 10:49
-
Sí, pasando int sin firmar y haciendo
va_arg(ap, int)
solo no es UB todavía. Pero de hecho es UB violar los requisitos de printf al esperar unint
. Aunque me parece una tontería. ¿Por qué no han especificado para printf: “El tipo del siguiente argumento será un int con o sin signo, y estará dentro del rango de int”.– Johannes Schaub – litb
18 de enero de 2010 a las 11:13
-
@Johannes: En realidad, es posible que ya esté especificado. Ver mi edición.
– Ant
18 de enero de 2010 a las 15:47
-
Pero si tiene una representación compatible no es importante, creo. Si el estándar dice que algo es UB, entonces es UB: no está reemplazado por alguna declaración en una nota al pie. Creo que esto solo significa que hay cierto rango de libertad que puede hacer, sin requisitos estrictos por parte del estándar. Al igual que con la lectura de diferentes miembros del sindicato (creo que esto no es UB por adelantado en C, pero puede definirse bien si ambos miembros tienen representaciones compatibles). Pero llamar a alguna función a través de una expresión de puntero de función sin prototipo con argumentos incompatibles sigue siendo UB, por ejemplo.
– Johannes Schaub – litb
18 de enero de 2010 a las 17:49
-
En este caso,
fprintf
la descripción indica: (para %d): “El argumento int se convierte en…” y “Si algún argumento no es del tipo correcto para la especificación de conversión correspondiente, el comportamiento no está definido”. Así que no creo que esté bien definido. ¿Quizás alguien de Usenet lo sepa?– Johannes Schaub – litb
18 de enero de 2010 a las 17:51
kennytm
En una implementación típica donde int
es de 32 bits, -1 cuando se convierte a un unsigned int
es 4,294,967,295 que es de hecho ≥ 1000.
Incluso si tratas la resta en un unsigned
mundo, 1000 - (4,294,967,295) = -4,294,966,295 = 1,001
que es lo que obtienes.
Es por eso gcc
escupirá una advertencia cuando compares unsigned
con signed
. (Si no ve una advertencia, pase el -Wsign-compare
bandera.)
-
Voté negativamente por “4,294,967,295 (complemento de 2)”. No tiene nada que ver con el complemento a 2. Dará el mismo valor en una máquina de complemento a 1. Y producirá un valor diferente en un entero de ancho de bits diferente.
– Johannes Schaub – litb
18 de enero de 2010 a las 9:58
-
@Schaub: Tal vez no esté claro, pero lo que quiero decir es que 4,294,967,295 (que es el complemento de 2 de 1) es de hecho ≥1. Además, la máquina de complemento a 1 la representación de -1 es 4.294.967.294.
– kennytm
18 de enero de 2010 a las 10:09
-
como dice litb, no tiene nada que ver con la representación. En una máquina de complemento a 1, convertir -1 en resultados sin signo en UINT_MAX, no da como resultado que se reinterprete el patrón de bits de complemento a 1. Una de las varias formas en que el complemento a 2 es conveniente es que las conversiones C (no) firmadas no cambian el patrón de bits. Eso es particular del complemento a 2: las conversiones de C a tipos sin signo se definen en términos de aritmética de módulo, no en términos de patrón de bits. En complemento a 1, la implementación tiene que hacer algo de trabajo real para llegar a UINT_MAX.
–Steve Jessop
18 de enero de 2010 a las 10:56
-
la edición es mejor, pero aún así no hay garantía de que
UINT_MAX
es 4.294.967.295. Consulte también stackoverflow.com/questions/1863153– Alok Singhal
18 de enero de 2010 a las 16:15
#include<stdio.h>
int main()
{
int a = 1000;
signed int b = -1, c = -2;
printf("%d",(unsigned int)b);
printf("%d\n",(unsigned int)c);
printf("%d\n",(unsigned int)a);
if(1000>-1){
printf("\ntrue");
}
else
printf("\nfalse");
return 0;
}
Para esto necesitas entender la precedencia de los operadores.
-
Los operadores relacionales funcionan de izquierda a derecha… así que cuando se trata
si (1000>-1)
luego, en primer lugar, cambiará -1 a un entero sin signo porque int se trata de forma predeterminada como un número sin signo y su rango es mayor que el número con signo
-1 cambiará al número sin firmar, cambia a un número muy grande
antti huima
Está haciendo una comparación sin signo, es decir, comparando 1000 con 2^32 – 1.
La salida está firmada debido a %d en printf.
NB: a veces, el comportamiento cuando mezcla operandos firmados y no firmados es específico del compilador. Creo que es mejor evitarlos y hacer yesos en caso de duda.
Encuentre una manera fácil de comparar, tal vez útil cuando no puede deshacerse de la declaración sin firmar (por ejemplo, [NSArray count]), simplemente fuerce el “int sin firmar” a un “int”.
Por favor, corríjame si estoy equivocado.
if (((int)a)>b) {
....
}
DigitalRoss
El hardware está diseñado para comparar firmado con firmado y sin firmar con sin firmar.
Si desea el resultado aritmético, primero convierta el valor sin signo a un tipo con signo más grande. De lo contrario, el compilador asumirá que la comparación es realmente entre valores sin signo.
Y -1 se representa como 1111..1111, por lo que es una cantidad muy grande… La más grande… Cuando se interpreta como sin signo.
naveen kumar
al comparar a>b donde a es de tipo int sin signo y b es de tipo int, b es tipo casted a unsigned int por lo tanto, el valor int firmado -1 se convierte en el valor MAX de unsigned**(rango: 0 a (2^32)-1)** Por lo tanto, a>b, es decir, (1000>4294967296) se vuelve falso. Por lo tanto, el bucle más printf(“a es PEQUEÑO! %d\n”, ab); ejecutado.
-
Este es un duplicado de menor esfuerzo de la respuesta más votada
– emsimpson92
09/07/2018 a las 23:00
Si usa el indicador del compilador -Wsign-compare, recibirá una advertencia para la comparación. Siempre debe usar -Wall (que incluye -Wsign-compare). Ver aquí para conocer otras formas de evitar este problema.
– Alejandro
5 de diciembre de 2010 a las 20:58
Consulte esta publicación para obtener más información: stackoverflow.com/q/10474769/844882
– Adrián Monk
7 de mayo de 2012 a las 1:44
@ Aleph7 – técnicamente eso no es del todo correcto, -Wsign-compare es solo inc. con -Pared Si Estás compilando C++. No está incluido para C (ver aquí gcc.gnu.org/onlinedocs/gcc/Warning-Options.html) Lo probé y puedo confirmar que el código anterior no da ninguna advertencia con -Wall pero sí con -Wsign-compare (estoy usando gcc (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010)
– bph
10 de noviembre de 2016 a las 20:09