Escribir software es un placer. Un programa no sólo debe funcionar bien y ser eficiente (esto se da por supuesto), sino que también debe ser bello y elegante de leer, comprensible y fácil de mantener, tanto para el autor como para eventuales futuros lectores. Programar bien en C es un arte.
En los títulos y los textos vais a encontrar unas cuantas citaciones cinematográficas (y si, soy un cinéfilo). Si no os interesan podéis fingir no verlas, ya que no son fundamentales para la comprensión de los post...
Este blog es la versión en Español de mi blog en Italiano L'arte della programmazione in C. Espero que mis traducciones sean comprensibles...
lunes, 23 de diciembre de 2013
martes, 3 de diciembre de 2013
Bitwise operations: ep.II - El ataque de los shift cómo usar los operadores Bitwise en C - pt.2
Ok, como había prometido aquí, volvemos al tema del bitwise para describir algunos resultados inesperados y proporcionar algunos ejemplos sencillos.
Vamos a empezar con las dolientes notas: Como se mencionó en el anterior post, las multiplicación y división con shift, dependiendo del tamaño del tipo del operando y la presencia o ausencia del bit de signo, pueden dar resultados inesperados. Vamos a ver un ejemplo:
El caso 3, además, es aún más insidioso: hacer operaciones con signo (en los casos 1 y 2 utilicé variables unsigned en preparación del paso 3) y usando valores negativos, pueden pasar cosas extrañas: para la representación misma de los números negativos en binario (complemento a 2) el bit más a la izquierda (MSB) es el bit de signo, y, en este caso la operación de shift es machine-dependent: en función del tipo de CPU puede tener o no la extensión de signo (por defecto, por ejemplo, en las máquinas de Intel), por eso el shift del ejemplo puede dar el resultado esperado (-4/4 = -1) o un resultado completamente diferente (¿¿ -4/4 = 63 ??). Una vez más, ¡tener cuidado!
Y ahora es el momento de mostrar algunos ejemplos prácticos de uso de lo anterior, que de otro modo seria know-how sin sentido: vamos a ver cómo leer el estado de cada bits de una word utilizando una máscara:
Fácil de leer y mantener: ya he escuchado esto... ¡ah! sí: es como debe ser todo el S/W que escribe un Analista Programador (y lástima que muchos analistas programadores con minúscula se olvidan de este imperativo cuando se sientan delante de un ordenador...).
¡Hasta el próximo post!
Vamos a empezar con las dolientes notas: Como se mencionó en el anterior post, las multiplicación y división con shift, dependiendo del tamaño del tipo del operando y la presencia o ausencia del bit de signo, pueden dar resultados inesperados. Vamos a ver un ejemplo:
El caso 1 es claramente el caso funcionante: un int es mucho más grande que los 8 bits utilizados para representar el número inicial (74), por lo que el shift no pierde ningún 1 en el lado izquierdo (este es el posible problema) y el resultado es correcto (74x4 = 296). Sin embargo, si utilizamos (caso 2) un char (8 bits) durante el shift perdemos un 1 y el resultado va en overflow (¿¿ 74x4 = 40 ??). Así que ¡tengan cuidado!void main() { // 1) SHIFT hacia la izquierda con unsigned int unsigned int a, b; a = 74; // 0 0 1 0 0 1 0 1 0 b = a << 2; // 1 0 0 1 0 1 0 0 0 resultado b=296 printf("var = %d; var << 2 = %d\n", a, b); // 2) SHIFT hacia la izquierda con unsigned char unsigned char c, d; c = 74; // 0 1 0 0 1 0 1 0 d = c << 2; // 0 0 1 0 1 0 0 0 resultado d=40 printf("var = %d; var << 2 = %d\n", c, d); // 3) SHIFT hacia la derecha con signed char (o int) char e, f; e = -4; // 1 1 1 1 1 1 0 0 f = e >> 2; // 1 1 1 1 1 1 1 1 resultado f=-1 printf("var = %d: var >> 2 = %d\n", e, f); // N.B.: sin extensión de signo sería: // e = -4; // 1 1 1 1 1 1 0 0 // f = e >> 2; // 0 0 1 1 1 1 1 1 resultado f=63 }
El caso 3, además, es aún más insidioso: hacer operaciones con signo (en los casos 1 y 2 utilicé variables unsigned en preparación del paso 3) y usando valores negativos, pueden pasar cosas extrañas: para la representación misma de los números negativos en binario (complemento a 2) el bit más a la izquierda (MSB) es el bit de signo, y, en este caso la operación de shift es machine-dependent: en función del tipo de CPU puede tener o no la extensión de signo (por defecto, por ejemplo, en las máquinas de Intel), por eso el shift del ejemplo puede dar el resultado esperado (-4/4 = -1) o un resultado completamente diferente (¿¿ -4/4 = 63 ??). Una vez más, ¡tener cuidado!
Y ahora es el momento de mostrar algunos ejemplos prácticos de uso de lo anterior, que de otro modo seria know-how sin sentido: vamos a ver cómo leer el estado de cada bits de una word utilizando una máscara:
sencillo, ¿no? Y lo mismo se puede hacer con una macro:void main() { // uso de una mascara para leer los bit de una word unsigned char mask = 1; // 0 0 0 0 0 0 0 1 unsigned char word = 74; // 0 1 0 0 1 0 1 0 // loop de lectura int i; for (i = 0; i < 8; i++) printf("el bit %d de la word es %s\n", i, (word & mask<<i) ? "ON" : "OFF"); }
Y luego, ya que esto es un blog de estilo, vemos una manera con una buena estética para leer lo input de un dispositivo, por ejemplo los finales de carrera de un sistema electromecánico que tenemos que controlar con nuestro querido C:#define INPUT(w, i) (w & 0x01<<i) void main() { // uso de una macro para leer los bit de una word unsigned char i_word = 74; // 0 1 0 0 1 0 1 0 // loop de lectura int i; for (i = 0; i < 8; i++) printf("el bit %d de la word es %s\n", i, INPUT(i_word, i) ? "ON" : "OFF"); }
Aquí, el ejemplo que acabamos de enseñar indica una manera, simple y elegante para describir los input (utilizando unos mnemónicos auto-explicativos) que pueden ser útiles para escribir el Software de control de dispositivos Hardware, fácil de leer y mantener.#define INPUT_FC1 (in_word & 0x01<<0) #define INPUT_FC2 (in_word & 0x01<<1) void main() { // uso de una define para leer un bit en una word unsigned char in_word = 74; // 0 1 0 0 1 0 1 0 // lectura printf("el bit FC1 de la word es %s\n", INPUT_FC1 ? "ON" : "OFF"); printf("el bit FC2 de la word es %s\n", INPUT_FC2 ? "ON" : "OFF"); }
Fácil de leer y mantener: ya he escuchado esto... ¡ah! sí: es como debe ser todo el S/W que escribe un Analista Programador (y lástima que muchos analistas programadores con minúscula se olvidan de este imperativo cuando se sientan delante de un ordenador...).
¡Hasta el próximo post!
Suscribirse a:
Entradas (Atom)