2011-09-26 26 views
5

Encontramos que se producen algunos valores extraños, a continuación se muestra un pequeño caso de prueba. Esto imprime "FFFFFFFFF9A64C2A". Lo que significa que el largo sin signo parece haber sido extendido. ¿Pero por qué? Todos los tipos a continuación no tienen firma, entonces, ¿qué está haciendo la extensión de señal? La salida esperada sería "F9A64C2A".Extensión de signo con largo largo sin signo

#include <stdio.h> 

int main(int argc,char *argv[]) 
{ 
    unsigned char a[] = {42,76,166,249}; 

    unsigned long long ts; 
    ts = a[0] | a[1] << 8U | a[2] << 16U | a[3] << 24U; 

    printf("%llX\n",ts); 


    return 0; 

} 

Respuesta

5

En la expresión a[3] << 24U, la a[1] tiene tipo unsigned char. Ahora, la "promoción entero" convierte a int porque:

lo siguiente puede ser utilizado en una expresión siempre que sea un int o unsigned int puede usarse :

[...]

Si un int puede representar todos los valores del tipo original, el valor se convierte en y int; de lo contrario, se convierte en unsigned int.

((draft) ISO/IEC 9899:1999, 6.3.1.1 2)

Tenga en cuenta también que los operadores de desplazamiento (que no sean la mayoría de los otros operadores) hacen no lo hacen las "conversiones aritméticas habituales" la conversión de los dos operandos a un tipo común . Pero

El tipo de resultado es el del operando izquierdo promocionado.

(6.5.7 3)

En una plataforma de 32 bits, 249 << 24 = 4177526784 interpretado como un int tiene su conjunto de bit de signo.

Simplemente cambiar a

ts = a[0] | a[1] << 8 | a[2] << 16 | (unsigned)a[3] << 24; 

corrige el problema (el sufijo U para las constantes no tiene impacto).

+0

Corrección secundaria: 'a [1]' tiene tipo 'char sin signo'. –

+0

@ user964970: leer nuevamente. El tipo de 'x << y' no tiene nada que ver con el tipo de' y'. –

+0

@Dietrich Epp: Gracias. –

1
 
ts = ((unsigned long long)a[0]) | 
    ((unsigned long long)a[1] << 8U) | 
    ((unsigned long long)a[2] << 16U) | 
    ((unsigned long long)a[3] << 24U); 

de fundición evita la conversión de los resultados intermedios a tipo int predeterminado.

+1

Pero * ¿por qué * hay un resultado int intermedio, cuando todos los tipos implicados son tipos sin firmar? El culpable parece ser solo el primer 'a [0]', reemplazando eso con '(unsigned) a [0]' y todo está bien. Pero por qué. – user964970

1

Algunos de los desplazados a [i], cuando se convierten automáticamente de unsigned char a int, producen valores de signo extendido.

Esto está de acuerdo con la sección 6.3.1 Opendos aritméticos, subsección 6.3.1.1 Booleanos, caracteres y enteros, del borrador C estándar N1570, que dice, en parte, "2. Lo siguiente puede usarse en una expresión siempre que se pueda usar una int int o unsigned: ... - Un objeto o expresión con un tipo entero (diferente de int o unsigned int) cuyo rango de conversión entero es menor o igual que el rango de int y unsigned int. .. Si un int puede representar todos los valores del tipo original ..., el valor se convierte en un int; de lo contrario, se convierte en un int sin signo. Se llaman las promociones enteras. ... 3. Las promociones enteras preservar el valor, incluido el signo ".

Véase, por ejemplo www.open-std.org/JTC1/SC22/WG14/www/docs/n1570.pdf

Usted podría usar un código como el siguiente, que funciona bien:

 int i; 
     for (i=3, ts=0; i>=0; --i) ts = (ts<<8) | a[i]; 
+0

Todos los [i] que se desplazan, en el código de ejemplo, tienen el lado derecho como no firmado debido al prefijo U en la constante. (por ejemplo, << 8U), lo que significa, p. la expresión a [1] << 8U ya debería tener el tipo sin signo, según esas reglas. – user964970

+0

@ user964970: El culpable no es 'a [0]'. Sin embargo, fundir 'a [0]' en 'unsigned' obliga al resultado de bitwise o ser' unsigned', que trunca la extensión de signo que aparece en 'a [3] << 24', que es el verdadero culpable. –

Cuestiones relacionadas