Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: NOB2014 en 16 Marzo 2014, 02:48 am



Título: Operadores a nivel de bits (lenguaje C).
Publicado por: NOB2014 en 16 Marzo 2014, 02:48 am
Hola a todos.
Estoy estudiando “Operadores a nivel de bits” y no sólo deseo aprender la sintaxis sino también interpretar lo que hace cada línea de código.-

Lenguaje: “C”

Problema: (dudas) Expuesta en el código. 
                                                                                 
Error: Ninguno.

Otras Observaciones: Ninguna.

Código:


Código
  1. # include <stdio.h>
  2.  
  3. void despriegaBits(unsigned valor);
  4.  
  5. int main(void){
  6. unsigned x; //No entiendo esta declaracion de variable, cual es el tipo?
  7. printf("\n\n Introduzca un entero sin signo....:");
  8. scanf("%u", &x);
  9.  
  10. despriegaBits(x);
  11.  
  12. return 0;
  13. }
  14.  
  15. void despriegaBits(unsigned valor){
  16. unsigned c; //No entiendo esta declaracion de variable, cual es el tipo?
  17.  
  18. unsigned despliegaMascara = 1 << 31; //Esta sentencia como se llama?, pareceria una funcion, pero no
  19.                                             //tiene parentesis, me desconcierta, y lo mas importante, como funciona?, que hace?.
  20.  
  21. printf("\n %10u = ", valor);
  22.  
  23. for(c = 1; c <= 32; c++){
  24. putchar(valor & despliegaMascara ? '1' : '0');
  25. valor <<= 1;
  26.  
  27. if(c % 8 == 0){
  28. putchar(' ');
  29. }
  30. }
  31. putchar('\n');
  32. }

Desde ya muchas gracias por la ayuda que puedan brindarnos.-
Saludos.
Daniel


Título: Re: Operadores a nivel de bits (lenguaje C).
Publicado por: engel lex en 16 Marzo 2014, 02:57 am
"unsigned" es una forma corta de "unsigned int" que es entero sin signo (solo positivos)

Código:
despliegaMascara = 1 << 31;

el >> y << sirven para mover un bit a la derecha o izquierda...

se usan igual que los operadores aritmeticos (a = 1 + 2)

se toma 1 en binario (que es igual 1) y se mueve 31 veces a la izquierda

Código:
1
10
100
1000
------
100000000000000000000000000000000
el valor en entero de esa variable no es importante en este caso... ya que se usará para una comparación en binario...

alguna otra duda?


por cierto! te llevas una buena cantidad de puntos por hacer un post bien explicado y con platilla y todo XD


Título: Re: Operadores a nivel de bits (lenguaje C).
Publicado por: NOB2014 en 16 Marzo 2014, 03:34 am
Hola angelx.
Sí, me quedan dudas sobre tu explicación, no porque este mal hecha( todo lo contrario) solo que siempre tardo en que me caiga la ficha, cuando esto ocurra los volveré a consultar.-

Saludos.
Daniel


Título: Re: Operadores a nivel de bits (lenguaje C).
Publicado por: engel lex en 16 Marzo 2014, 03:58 am
ok ;) no hoy problema... de toda formas dejo unos datos más


Código:
paso a paso las operaciones serían
a = 5 << 6;

      101
     1010
    10100
   101000
  1010000
 10100000
101000000

resultado: a -> B101000000 (B por binario)
---------------------------------------
a = 84 >> 3;
1010100
 101010
  10101
   1010

resultado: a->B1010
en este segundo caso los bits mas bajos simplemente desaparecen
---------------------------------------
a = 5;
a =<< 3; (esta expresion es similar a decir a = a<<3)
   101
  1010
 10100
resultado: a->B10100
---------------------------------------
a = 5 & 3
esto es un and lógico normal
101
011
___
001
resultado: a->1
---------------------------------------
a = 86 | 34
esto es un or lógico inclusivo
1010110
 100010
_______
1110110
resultado: a->B1110110
---------------------------------------
a = 86 ^ 34
esto es un or lógico exclusivo (o xor)
1010110
 100010
_______
1110100
resultado: a->B1110100



ahí tienes operadores binarios y su uso :P


Título: Re: Operadores a nivel de bits (lenguaje C).
Publicado por: rir3760 en 16 Marzo 2014, 15:10 pm
Un problema con ese programa es asumir que el tipo unsigned int esta constituido por 32 bits (si son mas el resultado es incorrecto, si son menos el programa reventara por el desplazamiento).

Una mejor aproximación consiste en utilizar el operador sizeof para obtener el numero de bytes y la macro CHAR_BIT para el numero de bits:
Código
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <limits.h>
  4.  
  5. void imprimir(unsigned num);
  6.  
  7. int main(void)
  8. {
  9.   unsigned num;
  10.  
  11.   printf("Introduce un entero no negativo: ");
  12.   fflush(stdout);
  13.   if (scanf("%u", &num) != 1)
  14.      return EXIT_FAILURE;
  15.  
  16.   imprimir(num);
  17.  
  18.   return EXIT_SUCCESS;
  19. }
  20.  
  21. void imprimir(unsigned num)
  22. {
  23.   unsigned msb = 1U << sizeof(unsigned) * CHAR_BIT - 1;
  24.   size_t i;
  25.   size_t j;
  26.  
  27.   for (i = 0; i < sizeof(unsigned); i++){
  28.      for (j = 0; j < CHAR_BIT; j++){
  29.         putchar(num & msb ? '1' : '0');
  30.         msb >>= 1;
  31.      }
  32.  
  33.      putchar(' ');
  34.   }
  35.   putchar('\n');
  36. }

Un saludo


Título: Re:
Publicado por: ivancea96 en 16 Marzo 2014, 15:54 pm
Si son menos, el programa no revienta, simplemente se pierden los bits :o

Enviado desde mi ST21i mediante Tapatalk


Título: Re: Operadores a nivel de bits (lenguaje C).
Publicado por: rir3760 en 16 Marzo 2014, 16:20 pm
Un problema con ese programa es asumir que el tipo unsigned int esta constituido por 32 bits (si son mas el resultado es incorrecto, si son menos el programa reventara por el desplazamiento).

Si son menos, el programa no revienta, simplemente se pierden los bits
No (por el desplazamiento). Para explicarlo mejor considerando que el numero de iteraciones y el desplazamiento esta dado por "magic numbers":
Código
  1. unsigned despliegaMascara = 1 << 31;
  2.  
  3. /* ... */
  4.  
  5. for(c = 1; c <= 32; c++) ...

Si el numero de bits del tipo unsigned int es:
A) Mayor (por ejemplo 64) entonces solo se procesa una parte de los bits.
B) Menor (por ejemplo 16) entonces el desplazamiento "1 << 31" causara que el programa reviente ya que el segundo operando debe ser mayor que cero y menor que el numero de bits del objeto.

Un saludo


Título: Re: Operadores a nivel de bits (lenguaje C).
Publicado por: Yoel Alejandro en 16 Marzo 2014, 17:15 pm
¿Qué se quiere decir exactamente con que el programa "revienta"? ¿Fuga de memoria, terminación anormal?

Hice una prueba, tomé el unsigned 1 y fui desplazando sus bits a la izquierda e imprimiendo el resultado por bits (con las mismas funciones dadas aportadas en el tema), para observar lo que ocurre luego del desplazamiento número 31.

Para mayor seguridad mandé imprimir antes la cantidad de bits de la representación de enteros en mi máquina y verificar que son 32. El resultado del programa:

En esta maquina los enteros ocupan 32 bits
29: 00100000 00000000 00000000 00000000
30: 01000000 00000000 00000000 00000000
31: 10000000 00000000 00000000 00000000
32: 00000000 00000000 00000000 00000000
33: 00000000 00000000 00000000 00000000

Se aprecia que en los desplazamientos 32 en adelante simplemente se pierde el bit y el número queda compuesto por ceros, pero la consola no me indica terminacióna anormal. Consulté el estado de salida del proceso (echo $?, para bash) y fue 0 (normal).

¿Qué me dicen ustedes?

====================
(EDITO) Ya comprendí el dilema, creo que no estamos hablando de lo mismo. La instrucción "num << 31" tendrá un comportamiento indefinido (pero no un error fatal) si el operando a la derecha es mayor que la longitud en bits del operando a la izquierda. Lo que yo hice fue diferente, ejecutar "num << 1" reiterativamente más de 31 veces.
====================

Código
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <limits.h>
  4.  
  5. void imprimir(unsigned num);
  6.  
  7. int main( ) {
  8.  
  9.   unsigned num = 1;
  10.   int i;
  11.  
  12.   printf("En esta maquina los enteros ocupan %d bits\n",
  13.          sizeof(unsigned) * CHAR_BIT);
  14.  
  15.   for (i = 0; i < 34; num <<= 1, i++ )
  16.      if ( i > 28 ) {
  17.         printf( "%d: ", i );
  18.         imprimir( (unsigned) num );
  19.      }
  20.  
  21.   return 0;
  22. }
  23.  
  24. void imprimir(unsigned num)
  25. {
  26.   unsigned msb = 1U << sizeof(unsigned) * CHAR_BIT - 1;
  27.   size_t i;
  28.   size_t j;
  29.  
  30.   for (i = 0; i < sizeof(unsigned); i++) {
  31.      for (j = 0; j < CHAR_BIT; j++) {
  32.         putchar(num & msb ? '1' : '0');
  33.         msb >>= 1;
  34.      }
  35.      putchar(' ');
  36.   }
  37.   putchar('\n');
  38. }


Título: Re: Operadores a nivel de bits (lenguaje C).
Publicado por: NOB2014 en 17 Marzo 2014, 00:27 am
Hola a todos.
Gracias a , ivancea96,Rir3760,yoel_alejandro por la ayuda, me interesa el código de estos último dos que luego de debelar las dudas que expongo a continuación voy a correrlos y estudiarlos.-

Código
  1. unsigned despliegaMascara = 1 << 31;
engelx ¿me podrías decir cuál es el valor final de despliegaMascara?, no logro mostrarlo con un printf y mil disculpas por la ignoracia al suponer que podía ser una función.-

Código
  1. putchar(valor & despliegaMascara ? '1' : '0');
En esta línea valor en cada iteración se mueve un bits a la izquierda ¿ con despliegaMascara no debería pasar lo mismo ?.-

Demás esta aclarar que la ayuda de cualquiera es bienvenida.-

Saludos.
Daniel 


Título: Re: Operadores a nivel de bits (lenguaje C).
Publicado por: engel lex en 17 Marzo 2014, 00:59 am
Citar
engelx ¿me podrías decir cuál es el valor final de despliegaMascara?
teoricamente el valor es 2147483648 pero eso no es lo importante, solo importa como binario...

despliegue mascara vale en binario
100000000000000000000000000000000


Citar
En esta línea valor en cada iteración se mueve un bits a la izquierda ¿ con despliegaMascara no debería pasar lo mismo ?

no despliegaMascara es simplemente para comprobar si hay un 1 en el bit más a la izquierda
por ejemplo con el numero 5

Código:
despliegueMascara = 100000000000000000000000000000000
5 en binario = 101
c= 1
100000000000000000000000000000000
000000000000000000000000000000101
recordando que esto es un and esto igual a 0...
 asi que simplemente colocaré varios pasos

c= 2
100000000000000000000000000000000
000000000000000000000000000001010 = 0

c= 3
100000000000000000000000000000000
000000000000000000000000000010100 = 0
.........
c= 29
100000000000000000000000000000000
010100000000000000000000000000000 = 0

c= 30
100000000000000000000000000000000
101000000000000000000000000000000 !=0 esto imprime un 1

c= 31
100000000000000000000000000000000
010000000000000000000000000000000 = 0

c= 32
100000000000000000000000000000000
100000000000000000000000000000000 !=0 esto imprime un 1

sabras que "?" es un if resumido

Código
  1. putchar(valor & despliegaMascara ? '1' : '0');

es lo mismo que
Código
  1. if(valor & despliegaMascara){
  2.    putchar('1');
  3. }else{
  4.    putchar('0');
  5. }


Título: Re: Operadores a nivel de bits (lenguaje C).
Publicado por: Yoel Alejandro en 18 Marzo 2014, 22:14 pm
Ok, creo NOMB2014 que la intención de la "máscara" en los programas anteriores era imprimir un número en binario (por eso putchar() que imprime carácter a carácter). Para que la explicación sea más sencill pondré un ejemplo con un char (un byte).

Si queremos imprimir en binario tenemos que escanear cada uno de sus bits e imprimir un '1' ó un '0' según el caso. Pongamos el ejemplo del numero:

num = 1000 1001

Entonces comenzamos con la máscara mask = 1000 0000 (0x80 en hexadecimal). Hacemos el AND binario entre el número y la máscara. Observa bien que la máscara tendrá un único bit en 1, los demás en 0. Si el AND binario da como resultado distinto de cero es porque el número original posee un '1' en la misma posición de la máscara. Así que el bit más significativo del número es '1':

num & mask = 1000 1001 & 1000 0000 = 1000 0000

Ahora seguimos recorriendo el número de izquierda a derecha, para ellos usamos la máscara mask = 0100 0000 (0x40 en hexadecimal). El resultado será '0', porque el número posee un bit '0' en la misma posición donde la máscara tiene su único 'bit 1':

num & mask = 1000 1001 & 0100 0010 = 0000 0000

Y así ya conoces el valor de los dos bits más significativos de num. Siguiendo con las máscaras 0010 0000,  0001 0000, etc., conocerás los demás bits.

Observa que las máscaras sucesivas se obtienen fácilmente empezando con 1000 0000 y rotando a la derecha tantas veces como uno menos que la cantidad total de dígitos binarios que posea el número.

Así, un programa para imprimir en binario un número char sería, donde por sencillez sustituyo el operador ternario "?:" por un "if" estándar:
Código
  1. void imprimir(unsigned char num)
  2. {
  3. unsigned char mask = 0x80; /* la mascara */
  4. size_t i;
  5.  
  6. for (i = 0; i < sizeof(char) * CHAR_BIT; i++) {
  7. if ( num & mask )
  8. putchar('1');
  9. else
  10. putchar('0');
  11. mask >>= 1;
  12. }
  13. }
  14.  

=============
OBSERVACIONES: 1. Una forma de inicializar la máscara hubiera sido mask = 0x01 << 7, o sea agarrar el 0x01 y rodar el '1' a la izquierda 7 veces. O más consistentemente, mask = 1 << sizeof(char) * CHAR_BIT - 1. Digo que la segunda forma es más consistente, porque se puede cambiar fácilmente el tipo char por short int, o por int donde el tamaño del número en bits es determinado automáticamente por el compilador en lugar de hacerlo nosotros manualmente.

2. El importante el tipo de la máscara como "unsigned". Pues según las especificaciones para enteros signados negativos el comportamiento de la operación ">>" queda indeterminado. Los bits desplazados en la parte izquierda del número pudieran ser rellenados por '1', o por '0' dependiendo del sistema.


Título: Re: Operadores a nivel de bits (lenguaje C).
Publicado por: NOB2014 en 19 Marzo 2014, 13:59 pm
Hola a todos.
Como siempre en este foro la ayuda es muy abundante y esmerada, con los últimos 2 post me queda bien claro como imprimir un número en binario, que es en definitiva lo que quería aprender.-
En definitiva voy a tratar de interpretar el código de rir3760, voy a correrlo, pero de principio no entiendo que valores arrojan size_t i y size_t j  y para que el doble for, ya me pongo a buscar información.-

Saludos.
Daniel


Título: Re: Operadores a nivel de bits (lenguaje C).
Publicado por: Yoel Alejandro en 20 Marzo 2014, 15:10 pm
Para abreviarte un poco el trabajo, lo que rir3760 es un doble "for" anidado para imprimir el número en grupos de a 8 bits (o sea, bytes), con un espacio entre cada byte, e.g.:

11000001 00010011 10100110 00010111

El for con el contador "j" imprime cada octeto y el for con el contador "i" pasa de un octeto a otro. Mira que la instrucción sizeof(unsigned) te da el tamaño de un unsigned (que es el mismo tamaño del int) en bytes, que es 4 para el caso de una máquina con enteros de 32 bits. Este es el rango de i. Luego, la macro CHAR_BIT da el número de bits que ocupa un byte, o sea 8 en nuestro caso. Este es el rango de j.

De no haber usado este anidamiento el número se hubiera impreso con todos los bits "pegados", o sea, menos estéticamente. Otra solución, con un solo contador, tal vez hubiera sido intercalar un espacio luego del bit 7, luego del bit, 15, y luego del 23, con el operador módulo "%". Si el número más 1 es divisible por 8, dejar el espacio:
Código
  1. void imprimir(unsigned num)
  2. {
  3.  unsigned msb = 1U << sizeof(unsigned) * CHAR_BIT - 1;
  4.  size_t i;
  5.  size_t j;
  6.  
  7.  for (i = 0; i < sizeof(unsigned) * CHAR_BIT; i++){
  8.     putchar(num & msb ? '1' : '0');
  9.     msb >>= 1;
  10.  
  11.     /* aqui el espacio */
  12.     if ( i > 0 && (i + 1)%8 == 0 )
  13.        putchar(' ');
  14.  }
  15.  putchar('\n');
  16. }
  17.  
donde la condición i > 0 es para que no imprima espacio antes del bit número cero (el primero).

Por último, el tipo size_t es un tipo aritmético, normalmente un entero no signado, devuelto por el operador sizeof() y usado en funciones como strlen(), fread(), etc. Para decirlo de un modo sencillo, usamos ese tipo para enumerar cantidades "posiblemente grandes" de bytes. En el caso de rir3760 se definió "i" de tipo size_t porque ese es el tipo devuelto por la expresión sizeof(unsigned).

Se que dijiste que ibas a investigar todo esto, y deseo que lo hagas pero no me costaba nada dedicar 5 minutos a aclarar unas cosas que te hubieran demandado quiźa 2 horas (y así usas esas 2 horas en investigar otros temas  ;D).


Título: Re: Operadores a nivel de bits (lenguaje C).
Publicado por: eferion en 20 Marzo 2014, 15:33 pm
Código
  1. if ( i > 0 && (i + 1)%8 == 0 )

O más sencillo.

Código
  1. if ((i + 1)%8 == 0 )

Todo i en el rango ( 0..31 ) que verifica "(i+1)%8==0" es necesariamente mayor que 0, puesto que (0+1)%8 == 1%8 == 1 != 0.

;)


Título: Re: Operadores a nivel de bits (lenguaje C).
Publicado por: rir3760 en 20 Marzo 2014, 17:20 pm
de principio no entiendo que valores arrojan size_t i y size_t j  y para que el doble for
Eso ya lo explico de una forma mas que satisfactoria yoel_alejandro.

----

En cuanto al condicional:
Código
  1. if ( i > 0 && (i + 1)%8 == 0 )
eferion ya indico como acortarlo, solo agregar que (por consistencia) se debe utilizar la macro CHAR_BIT:
Código
  1. if ((i + 1) % CHAR_BIT == 0) ...

Un saludo


Título: Re: Operadores a nivel de bits (lenguaje C).
Publicado por: NOB2014 en 20 Marzo 2014, 20:39 pm
Hola a todos.
Citar
Se que dijiste que ibas a investigar todo esto, y deseo que lo hagas pero no me costaba nada dedicar 5 minutos a aclarar unas cosas que te hubieran demandado quiźa 2 horas (y así usas esas 2 horas en investigar otros temas  ;D
No te das una idea lo que se estima estas frases cuando uno tiene 62 años y a nadie al alcance para pedir ayuda, lo digo más que nada por la seguridad que brinda el hecho de saber que podes levantar el tubo y tener la respuesta a tú duda o mandar un mail y del otro lado contar con un amigo que tiene todas las respuestas a tus inquietudes, bueno por algo será, por lo menos cuento con Uds.-
Gracias eferion y a vos rir3760 por la participación, en cuanto a este tema para mí lo podemos dar por solucionado.-

Saludos.
Daniel



Título: Re: Operadores a nivel de bits (lenguaje C).
Publicado por: Yoel Alejandro en 21 Marzo 2014, 15:16 pm
Correcto!