2011-09-29 26 views
14

El número 1, desplazado a la derecha por cualquier cosa mayor que 0, debería ser 0, ¿correcto? Sin embargo, puedo escribir este sencillo programa que imprime 1.¿Por qué (1 >> 0x80000000) == 1?

#include <stdio.h> 

int main() 
{ 
     int b = 0x80000000; 
     int a = 1 >> b; 
     printf("%d\n", a); 
} 

Probado con gcc en linux.

+27

Técnicamente, es un comportamiento indefinido, ya que está desplazando más del ancho del tipo (que gcc debe informar en una advertencia). –

+2

A menos que lo lances, ¿no sería eso pedirle que cambie por -1? – Marvo

+3

Además, parece que 'gcc' respeta por completo el espíritu de UB, dando diferentes resultados" incorrectos "según las condiciones (http://gcc.gnu.org/ml/gcc/2004-11/msg01131.html) –

Respuesta

30

6.5.7 bit a bit operadores de desplazamiento:

Si el valor del operando derecho es negativo o es mayor que o igual a la anchura de la operando de la izquierda promovido, el comportamiento es indefinido.

El compilador está en licencia para hacer cualquier cosa, obviamente, pero los comportamientos más comunes son para optimizar la expresión (y todo lo que depende de él) de distancia del todo, o simplemente dejar que el hardware subyacente hacer lo que hace por fuera cambios de rango Muchas plataformas de hardware (incluyendo x86 y ARM) enmascaran una cantidad de bits de bajo orden para usar como cantidad de desplazamiento. La instrucción de hardware real dará el resultado que está observando en cualquiera de esas plataformas, porque la cantidad de cambio está enmascarada a cero. Entonces, en su caso, el compilador podría haber optimizado el cambio, o simplemente dejar que el hardware haga lo que haga. Inspeccione el conjunto si desea saber cuál.

+0

@MooingDuck: No se puede deducir eso del comportamiento observado. Hay varias otras posibles explicaciones. –

+0

He oído que algunos compiladores implementan este comportamiento simplemente usando los cinco bits de la derecha del RHS, lo que explica el comportamiento del OP. –

+0

@StevemCanon: Tienes razón. Es pura especulación. –

2

de acuerdo con la norma, el cambio de más de los bits realmente existentes puede dar como resultado un comportamiento indefinido. Entonces no podemos culpar al compilador por eso.

La motivación probablemente reside en el "significado de frontera" de 0x80000000 que se encuentra en el límite del máximo positivo y negativo juntos (y que es "negativo" con el bit más alto) y en cierto control que se debe hacer y que el programa compilado no debe evitar perder tiempo verificando cosas "imposibles" (¿realmente quieres que el procesador cambie bits 3 mil millones de veces?).

+2

No "puede resultar", "no resulta". –

+0

@R ..: Depende del lado en que se encuentre: según el punto de vista del lenguaje, "sí resulta", (en el sentido de que el lenguaje no dice nada) por un punto de vista del compilador que "puede dar como resultado" (en el sentido de que el diseñador del compilador puede "definirlo") –

1

Es muy probable que no intente cambiar por una gran cantidad de bits.

INT_MAX en su sistema es probablemente 2**31-1, o 0x7fffffff (estoy usando ** para denotar exponenciación). Si ese es el caso, entonces en la declaración:

int b = 0x80000000; 

(que faltaba un punto y coma en la pregunta, por favor copiar y pegar su exacta código) la constante 0x80000000 es de tipo unsigned int, no int. El valor se convierte implícitamente en int. Como el resultado está fuera de los límites de int, el resultado está definido por la implementación (o, en C99, puede generar una señal definida por la implementación, pero no conozco ninguna implementación que lo haga).

La forma más común de hacerlo es reinterpretar los bits del valor sin signo como un valor firmado como complemento de 2. El resultado en este caso es -2**31 o -2147483648.

lo tanto, el comportamiento no está definido porque estás cambiando por valor igual o superior a la anchura del tipo int, no está definida porque estás cambiando de un (muy grande) valor negativo.

No es que importe, por supuesto; undefined no está definido.

NOTA: Lo anterior supone que int es de 32 bits en su sistema. Si int es más ancho que 32 bits, la mayor parte no se aplica (pero el comportamiento aún no está definido).

Si realmente quería intentar cambiar por 0x80000000 trozos, podría hacerlo de esta manera:

unsigned long b = 0x80000000; 
unsigned long a = 1 >> b; // *still* undefined 

unsigned long se garantiza que sea lo suficientemente grande como para contener el valor 0x80000000, por lo que evitar que parte del problema . Por supuesto, el comportamiento del cambio es tan indefinido como en el código original, ya que 0x80000000 es mayor o igual que el ancho de unsigned long. (A menos que su compilador tiene un muy grande unsigned long tipo, pero sin compilador del mundo real que lo hace.)

La única manera de evitar un comportamiento indefinido no es hacer lo que usted está tratando de hacer.

Es posible , pero es poco probable que sea probable, que el comportamiento del código original no está indefinido. Esto sólo puede ocurrir si la conversión definida por la implementación de 0x80000000 de unsigned int a int se obtiene un valor en el rango 0 .. 31. Si int es menor que 32 bits, es probable que el rendimiento 0.

+0

El último párrafo no es tan improbable: sería bastante probable en un sistema con 16 bit 'int', que está lejos de ser inaudito. – caf

+0

@caf: Muy bien, debería haber pensado en eso. –

0

bien leer que la conversión tal vez puede ayudar a

expresión 1 >> eXPRESIÓN2

El operador >> máscaras expression2 en evitar el desplazamiento de expresión 1 por el exceso.

Eso porque si la cantidad de desplazamiento excediera el número de bits en el tipo de datos de expresión1, todos los bits originales se desplazarían para dar un resultado trivial.

Ahora, para asegurar que cada turno deja al menos uno de los bits originales, los operadores de desplazamiento utilizar la siguiente fórmula para calcular la cantidad de desplazamiento real:

máscara expression2 (usando el operador AND) con uno menos que el número de bits en expression1.

Ejemplo

var x : byte = 15; 
// A byte stores 8 bits. 
// The bits stored in x are 00001111 
var y : byte = x >> 10; 
// Actual shift is 10 & (8-1) = 2 
// The bits stored in y are 00000011 
// The value of y is 3 
print(y); // Prints 3 

Ese "8-1" se debe a que x es de 8 bytes por lo que la operacion será con 7 bits. ese vacío elimina el último bit de los bits de cadena originales

Cuestiones relacionadas