2012-06-21 44 views
11

¿Cómo explica que la línea 7 reciba una advertencia, pero no la línea 5 o la línea 6?Tipo de advertencia de conversión después de operaciones bit a bit en C

int main() 
{ 
    unsigned char a = 0xFF; 
    unsigned char b = 0xFF; 
    a = a | b;      // 5: (no warning) 
    a = (unsigned char)(b & 0xF);  // 6: (no warning) 
    a = a | (unsigned char)(b & 0xF); // 7: (warning) 
    return 0; 
} 

GCC 4.6.2 de salida cuando se compila en una arquitectura de 32 bits (PC con Windows):

gcc -c main.c --std=c89 -Wall -Wextra -Wconversion -pedantic 
main.c: In function 'main': 
main.c:7:11: warning: conversion to 'unsigned char' from 'int' may alter its value [-Wconversion] 

Si esto ayuda a entender mi pregunta, aquí es cómo veo esto (probablemente incorrecta!) :

Supongo que en una máquina de 32 bits las operaciones se realizan en números de 32 bits. Dado que unsigned char cabe en 32-bit int, el resultado de la operación es 32-bit int. Pero desde GCC no da advertencias en las líneas 5 y 6, supongo que hay algo más en juego:

línea 5: GCC cifras que (UCHAR) O (UCHAR) nunca es mayor que MAX (UCHAR) , entonces no hay advertencia.

línea 6: GCC calcula que (uchar) Y 0xF nunca es mayor que MAX (uchar), entonces no hay advertencia. El lanzamiento explícito ni siquiera es necesario.

línea 7: Según las suposiciones anteriores: Y no debe dar aviso (ya que la línea 6), O tampoco debe dar aviso (desde la línea 5).

Supongo que mi lógica está defectuosa en algún lugar allí. Ayúdame a entender la lógica del compilador.

+3

Esto parece un error en el compilador: el compilador basado en clang en mac produce una compilación libre de advertencias con la configuración que ha especificado. – dasblinkenlight

+0

Sin advertencia de GCC 4.4.5 en Linux/x86-64. –

+0

¿Alguien puede confirmar que reciben la misma advertencia que yo? – Alex

Respuesta

0

Uso linux x86_64, GCC 4.70. Y obtén el mismo error Compilo el código y uso gdb para desensamblar el archivo de ejecución. Esto es lo que obtengo

(gdb) l 
1 int main(){ 
2  unsigned char a = 0xff; 
3  unsigned char b = 0xff; 
4  a = a | b; 
5  a = (unsigned char)(b & 0xf); 
6  a |= (unsigned char)(b & 0xf); 
7  return 0; 
8 } 
(gdb) b 4 
Breakpoint 1 at 0x4004a8: file test.c, line 4. 
(gdb) b 5 
Breakpoint 2 at 0x4004af: file test.c, line 5. 
(gdb) b 6 
Breakpoint 3 at 0x4004b9: file test.c, line 6. 
(gdb) r 
Starting program: /home/spyder/stackoverflow/a.out 

Breakpoint 1, main() at test.c:4 
4  a = a | b; 
(gdb) disassemble 
Dump of assembler code for function main: 
    0x000000000040049c <+0>: push %rbp 
    0x000000000040049d <+1>: mov %rsp,%rbp 
    0x00000000004004a0 <+4>: movb $0xff,-0x1(%rbp) 
    0x00000000004004a4 <+8>: movb $0xff,-0x2(%rbp) 
=> 0x00000000004004a8 <+12>: movzbl -0x2(%rbp),%eax 
    0x00000000004004ac <+16>: or  %al,-0x1(%rbp) 
    0x00000000004004af <+19>: movzbl -0x2(%rbp),%eax 
    0x00000000004004b3 <+23>: and $0xf,%eax 
    0x00000000004004b6 <+26>: mov %al,-0x1(%rbp) 
    0x00000000004004b9 <+29>: movzbl -0x2(%rbp),%eax 
    0x00000000004004bd <+33>: mov %eax,%edx 
    0x00000000004004bf <+35>: and $0xf,%edx 
    0x00000000004004c2 <+38>: movzbl -0x1(%rbp),%eax 
    0x00000000004004c6 <+42>: or  %edx,%eax 
    0x00000000004004c8 <+44>: mov %al,-0x1(%rbp) 
    0x00000000004004cb <+47>: mov $0x0,%eax 
    0x00000000004004d0 <+52>: pop %rbp 
    0x00000000004004d1 <+53>: retq 
End of assembler dump. 

la a = a | b se compila para

movzbl -0x2(%rbp),%eax 
or  %al,-0x1(%rbp) 

la a = (unsigned char)(b & 0xf) se compila para

mov %al,-0x2(%rbp) 
and $0xf,%eax 
mov %al,-0x1(%rbp) 

la a |= (unsigned char)(b & 0xf); se compila para

movzbl -0x2(%rbp),%eax 
mov %eax,%edx 
and $0xf,%edx 
movzbl -0x1(%rbp),%eax 
or  %edx,%eax 
mov %al,-0x1(%rbp) 

el lanzamiento explicto no apareció en el código asm. El problema es cuando se realiza la operación (b & 0xf). la salida de la operación es sizeof(int). lo que debe utilizar en su lugar:

a = (unsigned char)(a | (b & 0xF)); 

PS: fundido explict no puedan generar ninguna advertencia. incluso perderás algo.

+0

'movzbl -offset (% rbp),% eax' y' mov% al, -0x1 (% rbp) 'hacer los moldes de manera efectiva. Y no creo que el OP deba escribir su código de manera diferente. Es un código C válido y razonable. –

+0

@Alex mira esto 'o% al, -0x1 (% rbp)' y 'y $ oxf,% eax', la diferencia significa que el segundo necesita un lanzamiento explicto. y el tercer lanzamiento explícito no hace nada porque después de las instrucciones 'y $ 0xf,% edx', no hay operación de memoria para el resultado. – spyder

+0

En cualquier caso, no creo que este desmontaje demuestre nada. El código generado es correcto. –

0

Creo que el problema es que convierte int a unsigned char, Y de vuelta a int.

La línea 6 convierte int en unsigned char, pero solo la almacena en unsigned char.
La línea 7 convierte int en unsigned char, y luego, para hacer aritmética, la convierte nuevamente en int.El nuevo entero puede ser diferente del original, por lo que recibe una advertencia.

+0

¿Consideró la línea 5? Usando su lógica, ¿no debería la línea 5 recibir una advertencia? "La línea 5 toma' char sin signo 'y luego, para hacer aritmética, la convierte en 'int' [...]" – Alex

+0

Según las reglas de C, las 3 expresiones a la derecha del operador de asignación son de tipo 'int '. No son diferentes en ese sentido. –

+0

@Alex, solo la línea 7 se convierte de 'int' a' unsigned char' ** AND ** de nuevo a 'int'. – ugoren

0

El tipo de retorno del operador bit a bit & es entero. Siempre que inserte un int (4 bytes) en char o char sin signo (1 byte), se le advierte.

Por lo tanto, esto no está relacionado con el operador bit a bit y está relacionado con el tipo de transmisión de variable de 4 bytes a variable de 1 byte.

+0

Realmente no leíste mi publicación, ¿o sí? – Alex

+0

Estoy de acuerdo con Omer. Tuve el mismo problema que hace no hace mucho. El resultado de cualquier operación en modo bit se eleva al tamaño de un registro en C. si está en una máquina de 32 bits es de 4 bytes. – Alex

+0

@Alex Por favor, vuelva a leer mi pregunta. No estoy preguntando por qué el compilador da la advertencia. Estoy preguntando por qué trata las líneas 5/6/7 de manera diferente. Tu publicación solo indica lo obvio y no responde mi pregunta en absoluto. – Alex

1

Los compiladores son creados por personas y no tienen tiempo infinito para descubrir todas las posibilidades aritméticas para decidir, en cuyo caso vale la pena emitir una advertencia.

por lo que creo (opinión atención) que los ingenieros del compilador iría de la siguiente manera:

  • general, emitir una advertencia si el código se ve como si pudiera estar equivocado.
  • encuentra todos los casos obvios donde se puede corregir el compilador para que funcione con facilidad.
  • deje el resto de las advertencias como falsos positivos, porque la persona sabe lo que hace o se sentirá aliviado de que el compilador advierta.

Esperaría que las personas escriban código donde el resultado se convierte en (unsigned char) o donde el operador más externo enmascara todos los bytes más altos con una constante.

  • a = (unsigned char) (/* some obscure bit-wise expressoin */); estaría bien entonces
  • a = 0xff & (/* some obscure bit-wise expressoin */); también está bien

si sabe que su compilador traduce esos dos patrones correctamente los demás casos no debería molestarle demasiado.

que he visto compiladores que emitiría una advertencia debido a = a | b; por lo GCC no dar una advertencia es un bono gratis. podría ser, que gcc simplemente infiere la asignación constante en a | b y por lo tanto lo reemplaza con 0xff | 0xff que se sabe que funciona sin problemas. Si eso sucede, no sé por qué no puede derivar el valor constante de a en las otras declaraciones.

+0

+1 Por una buena respuesta , gracias! Mejor hasta ahora y si no voy a mejorar en algún momento, lo marcaré como una respuesta. – Alex

+1

Creo que el último párrafo de Alex contiene la información importante. gcc no arroja un error para la línea 5 porque realiza la operación 'OR' en tiempo de compilación y optimiza esa línea de código. Sospecho que si cambia las líneas 7 y 5, verá que el compilador lanza advertencias para diferentes partes de código. – bta

Cuestiones relacionadas