Mostrando entradas con la etiqueta C. Mostrar todas las entradas
Mostrando entradas con la etiqueta C. Mostrar todas las entradas

miércoles, 28 de agosto de 2013

La instrucción goto y etiquetas

El lenguaje C proporciona la instrucción goto como una forma de transferir el control del programa. Por ejemplo considere el siguiente código

/*+++++++++++++++++++++++++++++++++++++++++++++++++
+ Este programa hace uso de la proposicion goto   +
**************************************************/

#include <stdio.h>

int main()

{ /* Abre main */
printf("\nEsta es la primera instruccion del programa.");

/* Se usa goto */
goto instruccion;

printf("\nEsta es la segunda instruccion del programa.");

instruccion:
printf("\nEsta es la tercera instruccion del programa.");
printf("\nEsta es la cuarta instruccion del programa.\n");
return 0;

} /* Cierra main */


Aquí se presenta una instrucción goto seguido del nombre de una etiqueta llamada instruccion. La función de goto es omitir la realización de todas las líneas que hay entre goto y la etiqueta. Una ejecución del programa es la siguiente:

hernandez@miMaquina ~/Programas $ ./a.out 

Esta es la primera instruccion del programa.
Esta es la tercera instruccion del programa.

Esta es la cuarta instruccion del programa.

Observe que la segunda línea printf no se imprime, debido al uso de la etiqueta goto. A partir de donde está la etiqueta, se reinicia la ejecución del programa en el orden en que aparecen las líneas de código. El uso de goto puede ser muy cómodo, pero es muy poco recomendado. La programación estructurada ha venido a ser sinónimo de "programación sin goto". En el pasado los ingenieros y desarrolladores se dieron cuenta de que los programas con goto eran más difíciles de mantener y depurar, debido a que no hay un orden en las instrucciones.

lunes, 22 de julio de 2013

Kernighan_Ritchie_2_7.c (Inversión de bits)

______________________________________________________________________________________________________________________
2.7 Escriba una función invert(x, p,n) que regresa x con los n bits que principian en la posición p invertidos (esto es, 1 cambiado a 0 y viceversa), dejando los otros sin cambio.
______________________________________________________________________________________________________________________
Solución:
Primero, hay que dejar claro de lo que se trata éste programa. Para ésto, vamos a suponer que se recibe como entrada el número 90, y se quiere invertir los 3 bits que empiezan a partir del tercero. Lo que éste programa hace, se muestra en la siguiente figura:
Éste programa invierte los bits en rojo.
Se observa que hay sustitución de 1 por 0 a partir del tercer bit (el primero es el 0), por lo cual, el número se transforma en 42.

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *                                                                  + 
 * Este programa recibe un entero sin signo e  invierte los n bits  + 
 * de dicho entero a   partir de la posicion p.                     +
 *                                                                  + 
 * Recibe x, que en binario es:                                     + 
 *                                                                  + 
 * |x|x|x|x|x|x|x|x|                                                + 
 * en donde x es 1 o 0                                              + 
 *                                                                  + 
 * tambien se recibe p (la posicion a partir de la cual se invertiran
 * los bits)  y n, el numero de bits a invertir. Por ejemplo, si se *
 * recibe x = 30, n = 2, p = 2.                                     *
 *             p                                                    *
 *   30 = |1|1|1|1|0|                                               * 
 *           2 1                                                    *
 *           n                                                      *
 *                                                                  *
 * Este programa inviert (cambia 1 por 0 y viceversa) los n bits    *
 * desde p. (la cuenta es hacia la izquierda, el primer bit es el 0 *
 * Así que para este caso el numero quedaria como:                  *
 *                                                                  *
 *        |1|0|0|1|0| = 18                                          *
 *                                                                  *
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

/*******************************************************************+
 *                         ALGORITMO                                +
 * El problema se dividira en 2 partes principales                  +
 *                                                                  *
 * 1) Obtener el numero binario                                     *
 *                           p                                      *
 *    |0|0|0|0|...|0|x|x|...|x|0|...|0|                             *
 *                   p + n                                          *
 *                                                                  *
 *   donde los bits marcados con x son el complemento a 1 (inverso) *
 *   del numero entre 0 y p                                         *
 *                                                                  *
 * 2) Obtener el numero binario                                     *
 *                 p+n     n                                        *
 *    |x|x|x|...|x|0|0|...|0|x|....|x|                              *
 *                                                                  * 
 *    donde los bits marcados con x tienen el mismo valor que el    *
 *    numero original.                                              *
 *                                                                  *
 * El numero deseado se obtiene uniendo los que aparecen en 1) y 2) *
 * mediante el operador binario |                                   *
 * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
#include <stdio.h>

///////////////////////////////////////////////////////////////////////////
//UNSIGNED
/////////////////////////////////////////////////////////////////////////// 
unsigned invert(unsigned x,int p,int n)

{ /* Abre invert */
return (((~(~0 << p + n) & (~0 << p)) & ~x) | ((( ~0 << p+n) | ~(~0 <<p)) & x) ); 
} /* Cierra invert */

///////////////////////////////////////////////////////////////////////////
//MAIN
///////////////////////////////////////////////////////////////////////////

int main()
{ /* Abre main */
unsigned numero;
int a, b;

printf("\nPor favor introduzca un entero sin signo: \n");
scanf("%d", &numero);

printf("\nPor favor introduzca el numero de bits a invertir: \n");
scanf("%d", &b);

printf("\nPor favor introduzca el bit a partir del cual se hara la inversion: \n");
scanf("%d", &a);

printf("\nEl numero, despues de invertir los %d bits a partir de la posicion ", a);
printf("%d es: %d\n", b, invert(numero, b,a));

return 0;
}  /* Cierra main*/

miércoles, 8 de mayo de 2013

Kernighan_Ritchie_2.6 (Desplazamiento y sustitución de bits)

_______________________________________________________________________________________
2.6 Escriba una función setbits(x,p,n,y) que regresa x con los n bits que principian en la posición p iguales a los n bits más a la derecha de y, dejando los otros bits sin cambio.
_______________________________________________________________________________________
Solución:
Lo primero, igual que en este ejemplo, es entender bien el enunciado. Para ésto, vamos a visualizar la representación binaria de un par de números enteros positivos, digamos el 51 y el 42. Para estos números bastan seis bits. Vamos a suponer que el número de bits del primer entero (51) que vamos a cambiar por otros tantos del segundo (42) serán 3 (la variable n de setbits), y que además queremos cambiarlos a partir de la posición 1 (variable p ). Es importante recordar que la posición extrema derecha de un byte es el bit 0. Una vez establecido ésto, lo que el programa quiere es lo que se muestra en la siguiente figura:
La función setbits elimina los bits rojos y los sustituye por los azules.
La función setbits intercambia los 3 bits a partir del primero en la representación binaria de 51, con los tres primeros de la representación binaria de 42. El resultado es el número 53. Una vez entendida esta gráfica, es muy fácil entender la línea única de la función:

return (~((~0 << p) & ~(~0 <<(p+n))) & x ) | (~(~0 << p+n) & (y << p));

Esta línea, a pesar de parecer muy poco intuitiva, surge de una análisis simple.
1) Hacer 0 los bits entre p y p+n en el primer número. Ésto se logra creando una máscara que contenga 1s en todas las entradas, salvo en dichos bits. Esa máscara se crea con (~((~0 << p) & ~(~0 <<(p+n))) & x ). 2) Correr los bits del segundo número hasta la posición p. Ésto es, (y << p). 3) Hacer 0 los bits del segundo número, salvo en los intervalos 0-p y p+n hasta el último. Esto se logra con la máscara (~(~0 << p+n) & (y << p)). 4) Usar el operador | para lograr la unión de los bits distintos de 0 en de ambos números.
En los comentarios del programa se presenta éste mismo procedimiento como explicación del algoritmo.


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
* Este programa recibe dos enteros sin signo. Regresa otro entero sin    *
* signo, el que se obtiene al cambiar n bits del primero, a partir de la *
* posicion p, con los n bits mas a la derecha del segundo.               *
*                                                                        *
* Lo que recibe: 4 enteros positivos.                                    *
* Lo que regresa: 1 entero positivo.                                     *
*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*                               ALGORITMO:                               *
*Establecer a o los n bits del primer numero, a partir de la posicion p  *
*                                                                        *
*                                              p                         *
*      | x | x | x | x | x | x | 0 | 0 | ... | 0 | x | x | x |           *
*                              -     n   --------                        *
*                                                                        * 
*Mover los bits del segundo numero p posiciones a la izquierda           *
*                                                                        *
*Establecer a 0 todos los bits (antes de la posicion p y despues de la n)*
*del segundo numero                                                      *
*                                                                        *
*                                  n             p                       *
*      | 0 | 0 | 0 | 0 | ... | 0 | x | x | ... | x | 0 | 0 | 0 |         *
*                                                                        *
*Con el operador o inclusivo | (puede ser el ^ o exclusivo) obtener el   *
*numero buscado.                                                         *
*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#include 

///////////////////////////////////////////////////////////////////////
//setbits
///////////////////////////////////////////////////////////////////////

unsigned setbits( unsigned x, int p, int n, unsigned y)
{ /* Abre setbits */
 return (~((~0 << p) & ~(~0 <<(p+n))) & x ) | (~(~0 << p+n) & (y << p));
} /*Cierra setbits */

///////////////////////////////////////////////////////////////////////
//MAIN
///////////////////////////////////////////////////////////////////////

int main()
{  /* Abre main */
unsigned numero1, numero2;
int a, b;

printf("\nIntroduzca un entero sin signo: ");
scanf("%d", &numero1);

printf("\nIntroduzca un segundo entero sin signo: ");
scanf("%d", &numero2);

printf("\nIntroduzca el numero de bits a cambiar: ");
scanf("%d", &a);

printf("\nIntroduzca el bit a partir del cual se hara el cambio: ");
scanf("%d", &b);

printf("\nEl numero resultante es: %d\n", setbits(numero1, b, a, numero2));  

return 0; 
}  /* Cierra main */

ÉSte programa no tiene la precaución de verificar que los valores sean válidos. Por ejemplo, se podría introducir un valor de p mayor que el número total de bits. Ésto se puede evitar colocando algunos condicionales en la función main. No he querido colocarlos para no complicar el código. Es fácil hacerlo.
Aquí una ejecución, usando los números presentados anteriormente.

[hernandez@localhost Programas]$ ./a.out 

Introduzca un entero sin signo: 51

Introduzca un segundo entero sin signo: 42

Introduzca el numero de bits a cambiar: 3

Introduzca el bit a partir del cual se hara el cambio: 1

El numero resultante es: 53

__________________________________________________________________________________________
Esta entrada es parte de los problemas resueltos del libro El Lenguaje de Programación C de B. Kernighan y D. Ritchie
Entrada Anterior

Un ejemplo usando operadores para manejo de bits

Éste programa usa la función getbits (sección 2.9 de El Lenguaje de Programación C, de Kernighan y Ritchie). La razón para ponerlo aquí es que no es claro a primera vista, al menos para mí, lo que esa función hace. En la sección 2.9 se estudian los operadores binarios y getbits hace uso de &, el and de bits; <<, corrimiento a la izquierda; >>, corrimiento a la derecha y ~, complemento a uno, para hacer lo siguiente:
"regresa el campo de n bits de x (ajustado a la derecha) que principia en la posición p. Se supone que la posición del bit 0 está en el borde derecho y que n y p son valores positivos adecuados. Por ejemplo, getbits(x,4,3) regresa los tres bits que están en la posición 4, 3 y 2 ajustados a la derecha."

No soy el único que encuentra difícil la redacción (la original, en inglés) del libro de Kernighan y Ritchie. He encontrado varias páginas y foros en los que la gente se pregunta qué quisieron decir en tal o caul parte. Afortunadamente, aquí se puede entender sin ambigûedades.
Vamos a suponer que las variables son éstas: x = 179, p = 4 y n = 3. Para hacer ésto más fácil, la representación binaria de 179 es la siguiente:

Representación binaria de 179, en 10 bits.
Lo que hace nuestra función es mostrar dejar únicamente los 3 bits que comienzan a partir de la posición 4 (la posición extrema derecha es la 0). Éstos bits son 1 0 0, y deben aparecer corridos hacia la derecha. Todos los demás bits deben estar establecidos en 0.
El número 179 después de aplicar getbits.
El corazón del programa es, desde luego, la instrucción

return ((x >> (p+1-n)) & ~(~0 << n));

Ésta línea se divide en dos partes, separadas por el operador &. La instrucción x >> (p+1-n) corre hacia la derecha (4+1-3 = 2) bits. Esto es, se eliminan los bits 0 y 1 (ambos con 1), que deja el número binario 0000101100.
~(~0 << n) crea una máscara de bits. ~0 genera el binario 1111111111 y ~0 << n, con n = 3, corre hacia la izquierda el primer bit y llena los espacios con 0. Se produce entonces el número binario 1111111000. Finalmente, ~(~0 << n) crea el binario 0000000111. Esta es la máscara. Aplicando el operador & a 0000101100 y 0000000111 produce 0000000100. Este número es 4. Es precisamente lo que se obtiene al ejecutar el programa
/*++++++++++++++++++++++++++++++++++++++++++++
 * "regresa el campo de n bits de x (ajustado *
 * a la derecha) que principia en la posicion *
 * p. Se supone que la posicion del bit 0     *
 * esta en el borde derecho y que n y p son   *
 * valores positivos adecuados" Kernighan-    *
 * Ritchie                                    *
 *+++++++++++++++++++++++++++++++++++++++++++*/
#include 

///////////////////////////////////////
// FUNCION GETBITS
// ////////////////////////////////////

unsigned getbits(unsigned x, int p, int n)
{ /* Abre getbits*/
 
return ((x >> (p+1-n)) & ~(~0 << n));

} /*Cierra getbits*/

////////////////////////////////////////
//FUNCION MAIN
////////////////////////////////////////

int main()
{ /* Abre main*/

unsigned numero;
int a, b;

printf("\nIntroduzca un entero sin signo: ");
scanf("%d", &numero);

printf("\nIntroduzca el numero de bits a mostrar: ");
scanf("%d", &b);

printf("\nIntroduzca el numero de bit a partir del cual se mostraran: ");
scanf("%d", &a);

printf("El numero es: %d\n", getbits(numero,a,b));

return 0;
}/*Cierra main*/
Aquí la ejecución

[hernandez@localhost Programas]$ ./a.out 

Introduzca un entero sin signo: 179

Introduzca el numero de bits a mostrar: 3

Introduzca el numero de bit a partir del cual se mostraran: 4
El numero es: 4

jueves, 2 de mayo de 2013

De decimal a octal con una función recursiva

Éste programa es una consecuencia natural de la entrada anterior. Es tan simple como cambiar 2 por 8. En realidad, como puede verse, éste procedimiento sirve para convertir de decimal a cualquier base posicional entre 2 y 9. También es posible usarlo para una conversión a sistema hexadecimal.

#include <stdio.h>

/*////////////////////////////////////
 * Funcion Octal                     *
 *///////////////////////////////////*/

void Octal( int x)

{  /* Abre Octal*/
if ( x != 0 )
{ /* Abre if*/
Octal(x/8);

printf("%d\t", x%8);
} /*Cierra if */

else;
 /*printf("0\n");*/

}  /*Cierra Octal*/

/****************************************
 * main                                 *
 ***************************************/
int main()

{  /*Abre main */
 int num; 

 printf("\nIntroduzca un numero entero: ");
 scanf("%d", &num);

 Octal(num);
 printf("\n");

 return 0;
}  /*Cierra main */


Y aquí una ejecución, con el número 100 como entrada

[hernandez@localhost Programas]$ ./a.out 

Introduzca un numero entero: 100                                                     
1       4       4                   


miércoles, 1 de mayo de 2013

De decimal a binario con una función recursiva

Tengo una versión de éste programa que es bastante más complicada, pero por mucho tiempo recordé que lo había visto resuelto usando recursión. Ahora he pensado algunas formas de hacerlo, por ejemplo ésta:
Supongamos que tenemos el número 41, y que queremos convertirlo de su notación decimal a binario.
41 En binario  = 1 0 0 0 0 0 + (41 - 32) En binario

                                   9 En binario = 1 0 0 0 +  (9 - 8) En binario

                                                                1 En binario = 1.
El problema se va reduciendo cada vez por medio de llamadas a sí mismo con argumentos más simples. Al final sólo es necesario sumar las representaciones binarias de 32 + 8 + 1. Sin embargo no es ésto lo que yo quiero aquí. Además, sumar será en éste caso más complicado que la propia conversión. Por lo tanto descarté ésta posibilidad.
Otra forma es sugerida por el siguiente proceso, que puede encontrarse por ejemplo en el libro de Diseño Digital, de Morris Mano:
Número          División       Residuo          Binario
  41/2     =       40/2    +     1/2              1
  
  20/2     =       20/2    +      0               0

  10/2     =       10/2    +      0               0
 
   5/2     =        4/2    +     1/2              1
 
   2/2     =        2/2    +      0               0

   1/2     =          0    +     1/2              1
Éste algoritmo es fácilmente programable usando el operador %. Sin embargo, tiene el gran inconveniente de que los dígitos se imprimen en el orden inverso, como puede verse al ejecutar el siguiente código

#include <stdio.h>

/*////////////////////////////////////
 * Funcion Binario                   *
 *///////////////////////////////////*/

void Binario( int x)

{  /* Abre binario*/
if ( x != 0 )
{ /* Abre if*/
printf("%d\t", x%2);

Binario(x/2);

} /*Cierra if */

else;
 /*printf("0\n");*/

}  /*Cierra binario*/

/****************************************
 * main                                 *
 ***************************************/
int main()

{  /*Abre main */
 int num; 

 printf("\nIntroduzca un numero entero: ");
 scanf("%d", &num);

 Binario(num);

 return 0;
}  /*Cierra main */


La ejecución es la siguiente

[hernandez@localhost Programas]$ ./a.out 

Introduzca un numero entero: 41
1       0       0       1       0       1  

De nuevo el problema es que se está imprimiendo el número al revés. Ésto es grave, porque el código es muy bueno, y cualquier intento por corregir el defecto, requiere algoritmos más complicados que la propia conversión.
Sin embargo, después de pensarle, la solución es muy simple. En la función Binario, basta con cambiar el orden de los dos únicos enunciados. Primero hay que hacer una llamada a Binario, y ésto llevará la ejecución hasta el final, hasta el caso más simple, y el control se regresará desde el último caso, imprimiendo los dígitos binarios en el orden inverso. El programa correcto es el siguiente:

#include <stdio.h>

/*////////////////////////////////////
 * Funcion Binario                   *
 *///////////////////////////////////*/

void Binario( int x)

{  /* Abre binario*/
if ( x != 0 )
{ /* Abre if*/
Binario(x/2);

printf("%d\t", x%2);
} /*Cierra if */

else;
 /*printf("0\n");*/

}  /*Cierra binario*/

/****************************************
 * main                                 *
 ***************************************/
int main()

{  /*Abre main */
 int num; 

 printf("\nIntroduzca un numero entero: ");
 scanf("%d", &num);

 Binario(num);

 return 0;
}  /*Cierra main */


La ejecución,
[hernandez@localhost Programas]$ ./a.out 

Introduzca un numero entero: 41
1       0       1       0       0       1
Comopuede verse, son muy pocas las líneas que se requieren para hacer la conversión de binario a decimal usando la recursión, y es un cambio mínimo el que hace esta diferencia.

viernes, 26 de abril de 2013

El Operador ~ de bits

El operador ~ de bits, también llamado operador de complemento a uno, actúa sobre tipos enteros (char, int, shor, long) a nivel de bits. Lo que hace es cambiar 0 por 1 y viceversa. Por ejemplo, si se tiene el la constante int x = 47 en una representación binaria de 6 bits, 101111, la instrucción ~x da como resultado el número 010000. Hay que tener cuidado antes de apresurarnos a decir cual será el resultado aplicando éste operador en un programa en C. El punto importante aquí es que si nosotros imprimimos el valor actual de x, mediante una instrucción printf, obtendremos -48.

La acción del operador complemento a uno
Sea el siguiente programa


/*+++++++++++++++++++++++++++++++++++++++++++++
 * Este programa cambia un numero sin sigo a  +
 * su complemento a uno a nivel de bits       +
 * ++++++++++++++++++++++++++++++++++++++++++*/

#include 

int main()
{  /* Abre main*/
unsigned contador;
unsigned x;
unsigned temporal;

printf("\nIntroduzca un número: ");
scanf("%d", &x);

temporal = ~x;

printf("\nEl complemento a uno de %d es: %d\n", x, temporal);

printf("\nEl valor final de x: %d\n", x);

return 0;
}  /* Cierra main*/

Una ejecución con 47 como entrada es la siguiente:


Introduzca un número: 47

El complemento a uno de 47 es: -48

El valor final de x: 47

Si las computadoras representaran los enteros con signo mediante el complemento a 1, el número al aplicar ~x sería -47, sin embargo, las computadoras almacenan los enteros con signo mediante la representación en complemento a dos. En ésta entrada hay más información al respecto.

miércoles, 24 de abril de 2013

El operador & de bits

El operador &, llamado AND de bits, compara dos números de tipo entero (int, short, long, char) bit por bit, dando por resultado un tercer número, que se forma al colocar un 0 ó 1 en el bit correspondiente a la comparación realizada de acuerdo con la siguiente regla: si los dos bits son iguales a 1, entonces se obtiene 1, de otra forma se obtiene un 0. Sean, por ejemplo, los números 19 y 25, que en representación binaria de 5 bits se escriben 19 = 1 0 0 1 1, 25 = 1 1 0 0 1. Si se realiza la comparación, se verá que los bits de la derecha son los dos iguales a 1, por lo tanto el número resultante tendrá un 1 en el bit de la extrema derecha. De manera similar, el segundo bit de 19 tiene un 1, en tanto que el de 25 tiene un 0, el resultado, por lo tanto, es 0. La comparación completa aparece en la figura de abajo.

El operador lógico & de bits.
El operador & también es utilizado para hacer referencia a una localidad de memoria,por ejemplo en la función scanf. Los usuarios de C++, que utilizan un lenguaje orientado a objetos, le llamarían a ésto una sobrecarga: el mismo operador se utiliza en situaciones distintas para realizar distintas tareas. La razón por la cual & no puede operar sobre tipos flotantes es porque la representación de números enteros es distinta a la de los tipos float. El programa que aparece abajo hace uso de el operador de bits &.

/*+++++++++++++++++++++++++++++++++++++++*
*Este programa recibe un par de numeros  *
*e imprime el resultado de aplicarles el *
*operador binario &                      *
*+++++++++++++++++++++++++++++++++++++++*/

#include 

int main()
{   /*Abre main*/
 int x; 
 int y;
 
 printf("\nEste programa hace uso del operador binario &\n");
 printf("\nIntroduzca un entero: ");
 scanf("%d", &x);
 printf("\nIntroduzca un segundo entero: ");
 scanf("%d", &y);

 printf("\n%d & %d = %d\n", x, y, x&y);

 return 0;
}   /*Cierra main*/

Una ejecución del programa, co los números 19 y 25 es:

Este programa hace uso del operador binario &                                        
                                                                                     
Introduzca un entero: 19                                                             
                                                                                     
Introduzca un segundo entero: 25                                                     
                                                                                     
19 & 25 = 17  

En ocasiones es conveniente fijar una constante entera mediante un número binario, por ejemplo para crear una máscara de bits. La forma de hacer ésto en C es escribiendo la constante en sistema de numeración octal o en hexadecimal. Toda constante que es precedida por un 0, como 0177, es tomada como una cantidad octal (127 en éste caso); en tanto que las cantidades precedidas de 0x ó 0X, como 0x7F (también igual al 127 decimal) son cantidades hexadecimales. En particular el número 0x7F se representa, en notación binaria, como 01111111 por lo cual, una instrucción como x & 0x7F, hará 0 todos los bits de x, salvo los primeros 7 de la derecha.

domingo, 18 de noviembre de 2012

Operadores y Máscaras de Bits

Las computadoras almacenan datos en forma de 0s y 1s. La razón de ésto es que el binario es el sistema de numeración posicional más simple. Un bit es la menor cantidad de información que se puede almacenar. Un conjunto de 8 bits recibe el nombre de palabra o byte. El lenguaje de programación C es un lenguaje de nivel medio. Ésto, entre otras cosas, significa que es posible realizar operaciones a nivel de bits. Ésta es una interacción directa con el hardware y por lo tanto es mucho más rápida que la programación a alto nivel. Por ejemplo, es posible determinar el tamaño en bytes de cualquier tipo de datos con
=====================================================================================
El operador unario sizeof()
=====================================================================================
Vamos a considerar el siguiente programa

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *                                                         +
 * Este programa imprime los tamanios en bits de variables +
 * tipo char, int, double, etc.                            + 
 *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#include 
#define TAMANO 2

int main()
{  /* Abre main*/
char c;
int x;
int Arreglo[TAMANO];
double y;

printf("\nEl tamanio en bytes de una variable char es: %d\n", sizeof(c));
printf("El tamanio en bytes de una variable int es: %d\n", sizeof(x));
printf("El tamanio en bytes de una arreglo int de %d localidades es: %d", TAMANO, sizeof(Arreglo));
printf("\nEl tamanio en bytes de una variable double es: %d\n", sizeof(y));

return 0;
}  /* Cierra main*/

La ejecución del mismo produce

El tamanio en bytes de una variable char es: 1
El tamanio en bytes de una variable int es: 4
El tamanio en bytes de una arreglo int de 2 localidades es: 8
El tamanio en bytes de una variable double es: 8
Cuando el operador sizeof se aplica a una variable char, el resultado es 1 porque 1 byte (8 bits) es el tamaño necesario para almacenar un carácter cualquiera de los 256 que forman el código ASCII. Así que por esa razón el tamaño de una variable tipo carácter es estándar. El tamaño de un entero en C, depende de la máquina en la que se ejecuta; para ésta máquina en particular es de 4 bytes (32 bits) (véase la entrada La representación en complemento a 2 para más detalles.) También se ha definido un arreglo de 2 localidades enteras; el número de bytes que ocupa dicho arreglo es 8, cuatro para cada localidad entera. Adicionalmente aparece una variable tipo double, que ocupa 8 bytes (64 bits).
=====================================================================================
& El Operador AND de Bits
=====================================================================================
El operador & (NO confundir con el operador lógico &&) es un operador lógico a nivel de bits. Compara un par de cadenas de bits bit por bit, el resultado para cada comparación es 1 si los dos bits son 1 y 0 en otro caso. Como ejemplo, vamos a considerar las siguientes cadenas:
Una máscara de bits
Empezando por la derecha, el primer bit de la primera cadena, tiene un 1, y la segunda cadena tiene también un 1, por lo tanto, 1 & 1 = 1. Aplicando & a los bits de la siguiente posición: 0 & 0 = 0, los siguientes: 1 & 0 = 0; después 0 & 1 = 0; los bits de la 5ta posición: 1 & 1 = 1, y los últimos dan 0, 1 & 0 = 0.
Con el operador & se puede ocultar un conjuto de bits que no son relevantes en determinada situación. Ésto constituye una máscara de bits. Vamos a considerar un pequeño problema y elaborar una solución: En un ciclo controlado por una variable llamada TAMANO, es necesario imprimir el valor del contador sólo desde 0 hasta 15, y comenzar después en 0, aún cuando TAMANO se siga incrementando. Con algunas instrucciones condicionales dentro del ciclo es posible realizar esta tarea, pero con una máscara de bits, se puede escribir un programa como el siguiente:

/*+++++++++++++++++++++++++++++++++++++++++++++
 *                                            +
 * Este programa imprime ciclicamente valores +
 * de una variable por debajo de un limite,   +
 * por medio de una mascara de bits.          +
 * +++++++++++++++++++++++++++++++++++++++++++*/

#include 
#define TAMANO 200
#define LIMITE 15

int  main()

{ /* Abre main*/
int i = 0;

for ( i = 0; i < TAMANO; i++ )
{
 printf("%d\n", i & LIMITE);
}

return 0;
} /* Cierra main*/

La salida de este programa es: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, ...
Se ha mantenido la impresión por debajo de la variable LIMITE. La línea relevante, para los fines de esta entrada, es la siguiente:

printf("%d\n", i & LIMITE);

la función printf imprime un entero, el entero que se produce de la comparación de los bits de el primer número, el contador i, con el número LIMITE. Es importante mencionar que éste y los siguientes operadores son aplicables únicamente a variables de tipo entero, como char, int, long, short, con los calificativos signed y unsigned. En éste caso, la variable límite funciona como una máscara, que oculta todos los bits a la izquierda de el último 1 a la izquierda.
Máscara de bits para controlar un ciclo.
Ésta figura presenta los primeros 6 bits de los los números LIMITE (15) y un valor de TAMANO (25), recuerde que, en nuestra máquina, los enteros tienen tìpicamente 32 bits, o sea que hacia la izquierda de cada renglón se encuentran muchos ceros que no se han dibujado por comodidad. Aplicando el operador & bit por bit a ambas representaciones obtenemos el renglón de abajo, el cual es el número 9 en el sistema de numeración binario. Observe que más allá del último 1 (el de la extrema izquierda)de la máscara (15) todos los bits del resultado se vuelven 0, y si hubiera algún 0 antes del último 1 ocurriría una ambigüedad, ya que 0 ó 1 en TAMANO produciría 0. ÉSta es la razón por la cual éste método funciona solamente con números que en su representación binaria tengan sólo 1s, como 3 (11), 7 (111), 15 (1111), 31 (11111), etc.
=====================================================================================
<< El Operador de desplazamiento izquierdo de Bits
=====================================================================================
Vamos a considerar el siguiente programa.

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*  Este programa hace uso de el operador de desplazamiento *
*  hacia la izquierda <<                                   *
*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#include 
using namespace::std;

int main()
{   /* Abre main*/
int x = 1;
// Los operadores de bits solo se aplican a variables unsigned
cout <<"\nEl tamanio de una variable int es: "<< sizeof(int)<< endl;

cout <<"\nEl valor inicial de x: "<< x << endl;

for (unsigned i = 1; i <= 8; i++ ) 
{
  
unsigned desplazado = x << i;

cout <<"\nEl numero: " << desplazado << endl;
}

cout <<"\nEl valor final de x: " << x << endl;
}   /* Cierra main*/


La ejecución es la siguiente:

El tamanio de una variable int es: 4

El valor inicial de x: 1
El numero: 2
El numero: 4
El numero: 8
El numero: 16
El numero: 32
El numero: 64
El numero: 128
El numero: 256
El valor final de x: 1

Al principio se define una variable llamada x, la cual se inicializa con el valor 1. Un ciclo for, de 1 a 8 incluidos, utiliza el operador de desplazamiento izquierdo para recorrer los bits hacia la izquierda, lo cual, en el sistema posicional binario, hace que el valor se incremente en potencias de 2, tal como se muestra en la figura siguiente:

Observe que el valor de x, fuera del ciclo for, sigue siendo de 1.

=====================================================================================
>> El operador de desplazamiento derecho de bits
=====================================================================================
De manera similar, el lenguaje C proporciona el operador de desplazamiento derecho de bits >>. El siguiente programa muestra cómo opera:

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*  Este programa hace uso de el operador de desplazamiento *
*  hacia la derecha  >>                                    *
*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#include 
using namespace::std;

int main()
{   /* Abre main*/
int x = 128;

cout <<"\nEl tamanio de una variable int es: "<< sizeof(int)<< endl;

cout <<"\nEl valor inicial de x: "<< x << endl;

for (unsigned i = 1; i <= 8; i++ ) 
{
  
unsigned desplazado = x >> i;

cout <<"\nEl numero: " << desplazado << endl;
}

cout <<"\nEl valor final de x: " << x << endl;
}   /* Cierra main*/

La salida es la siguiente:

El tamanio de una variable int es: 4

El valor inicial de x: 128

El numero: 64
El numero: 32
El numero: 16
El numero: 8
El numero: 4
El numero: 2
El numero: 1
El numero: 0
El valor final de x: 128

Se inicia con una variable llamada x, la cual se inicializa en 128, en un ciclo for se va disminuyendo el valor de dicha variable por medio del operador de desplazamiento derecho. La variable adquiere sucesivamente los valores 128, 64, 32, 16, 8, 4, 2, 1. Una representación esquemática del acarreo de bits es la siguiente:


viernes, 14 de septiembre de 2012

Kernighan_Ritchie_2.10 (Convertir un texto a minúsculas)

___________________________________________________________________________________
2.10 Reescriba la función lower, que convierte letras mayúsculas en minúsculas, con una expresión condicional en vez de un if-else.
___________________________________________________________________________________
SOLUCIÓN: La modificación es directa. El programa está escrito de tal manera que se puede recibir como entrada un archivo de texto y convertirlo a minúsculas. La razón por la que éste programa sólo funciona con el código ASCII, es porque la línea principal de la función lower es la siguiente:

x = (c >= 'A' && c <= 'Z')? c + 'a' - 'A': c;

básicamente hace uso del hecho que en ASCII, las letras mayúsculas aparecen antes que las minúsculas, y la distancia entre una mayúscula y su correspondiente minúscula es de 32 posiciones. Así, el valor de A es 65, y el de a es 97; el de S es 83, y el de s 115. Por tal motivo, en la instrucción de asignación, primero se verifica que el carácter sea mayúscula, si lo es, entonces se cambia a el correspondiente valor más 32 ( escrito como c + 'a' -'A'), de lo contrario, se deja igual.


#include <stdio.h>

int lower(int s);


////////////////////////////////////////////////
// MAIN
////////////////////////////////////////////////

int main()
{ /*Abre main*/
int z, l;

printf("\nPor favor, introduzca un texto. ");
printf("Se imprimira en minusculas.\n");

while( (z = getchar()) != EOF)
{ /* Abre while */  
l = lower(z);
putchar(l);
}/*Cierra while */

return 0;
} /*Cierra main*/

/*//////////////////////////////////////////////
// LOWER
//////////////////////////////////////////////*/

int lower(int c)
{  /*Abre lower*/
int x;

x = (c >= 'A' && c <= 'Z')? c + 'a' - 'A': c;

return x;
}  /*Cierra lower*/


La ejecución con éste texto como entrada

ERASE UN HOMBRE A UNA NARIZ PEGADO,
ERASE UNA NARIZ SUPERLATIVA
UNASE UNA NARIZ ZAYON Y ESCRIBA,
ERASE UN PEJE ESPADA MUY BARBADO.

produce:

Por favor, introduzca un texto. Se imprimira en minusculas.

erase un hombre a una nariz pegado,
erase una nariz superlativa
unase una nariz zayon y escriba,
erase un peje espada muy barbado.

Kernighan_Ritchie_2.5 (Primera posición de la coincidencia de dos cadenas)

___________________________________________________________________________________
2.5 Escriba la función any(s1,s2), que regresa la primera posición de la cadena s1 en donde se encuentre cualquier carácter de la cadena s2, o -1 si s1 no contiene caracteres de s2. (La función de biblioteca estándar strpbrk hace el mismo trabajo pero regresa un apuntador a la posición encontrada).
___________________________________________________________________________________
Solución: El código siguiente presenta la solución del problema. Se ha puesto un par de cadenas, las cuales se pueden cambiar. Lo importante es la función any. La ejecución del programa, tal como está, produce -1 como salida, ya que la cadena "xxz" no tiene ningún elemento en común con "La felicidad esta en lugares mas o menos lejanos".

#include<stdio.h>
enum {TAMANO1 = 100, TAMANO2 = 50};

int any(char s1[], int n, char s2[], int m);
void Imprimir( char cadena[]);

/*//////////////////////////////////
// MAIN
//////////////////////////////////*/

int main()
{ /* Abre main */
int y;

char cadena1[TAMANO1] = "La felicidad esta en lugares mas o menos lejanos";
char cadena2[TAMANO2] = "xxz";
/*La cadena1 */
Imprimir(cadena1);
printf("\n");
/*La cadena 2*/
Imprimir(cadena2);
printf("\n");

y = any(cadena1, TAMANO1, cadena2, TAMANO2);

if (TAMANO1 != y)
printf("\n%d\n", y);
else 
printf("-1\n");
return;
} /* Cierra main*/

/*//////////////////////////////////
// ANY
//////////////////////////////////*/

int any(char s1[], int n, char s2[], int m)

{ /* Abre any*/
int i, j, x = TAMANO1; 

for (i = 0; s2[i] !=  '\0'; i++)   
for (j = 0; s1[j] !=  '\0'; j++)
{ /*Abre for*/
if (s1[j] == s2[i])
{ /* Abre if */
x = (j < x)?  j:x;
break; /*Salimos del for anidado*/
} /* Cierra if*/
} /* Cierra for*/

return x;
} /* Cierra any*/


/*///////////////////////////////////
// IMPRIMIR
///////////////////////////////////*/

void Imprimir(char cadena[])

{ /*  Abre Imprimir */
int i = 0; 
for (i = 0; cadena[i] != '\0'; i++)
putchar(cadena[i]); 

return;
} /* Abre Imprimir */

La ejecución de éste programa es la siguiente:

Cadena 1:
La felicidad esta en lugares mas o menos lejanos
Cadena 2:
xxz
-1

__________________________________________________________________________________________
Esta entrada es parte de los problemas resueltos del libro El Lenguaje de Programación C de B. Kernighan y D. Ritchie
Entrada anterior
Entrada siguiente
__________________________________________________________________________________________

jueves, 13 de septiembre de 2012

Kernighan_Ritchie_2.4 (Suprimir las coincidencias de dos cadenas)

___________________________________________________________________________________
2.4 Escriba una versión alterna de squeeze(s1,s2) que borre cada carácter de s1 que coincida con cualquier carácter de la cadena s2.
___________________________________________________________________________________
Solución:
La función squeeze a la que hace referencia el enunciado aparece en el texto y es la siguiente:


void squeeze(char s[], int c)
{
int i, j;

for(i = j = 0; s[i] != '\0'; i++)
   if (s[i] != c)
      s[j++] = s[i];

s[j] = '\0';
}

Esta función comprime la cadena s1 por medio de eliminar la variable c. La variable i recorre el arreglo que contiene s1 y la variable j recorre el mismo arreglo siempre que la variable leída no coincida con c. Al final, para indicar que la cadena ha terminado, se escribe explícitamente el carácter de terminación de cadena '\0'.
A continuación presento mi versión de ésta problema tal como lo pide el libro. He puesto un par de cadenas, la primera que dice: "La felicidad esta en lugares mas o menos lejanos, como Londres" y la segunda: "Hola". Lo que hace el programa, y concretamente la función squeeze, es recibir las dos cadenas (supone que la primera es mayor que la segunda) y recorrer todo el primer arreglo (el mayor) desde el elemento 0 hasta el último en busca de coincidencias. Al mismo tiempo va sustituyendo las variables encontradas en el mismo arreglo, sólo si no coinciden con alguna de las entradas de la segunda cadena. Para esto, se recorren también todos los elementos del segundo arreglo hasta encontrar el elemento terminal '\0'.

/*************************************************************************
*                                                                        *
* Este programa tiene como datos un par de cadenas: cadena y cadena2     *
* Suprime todas las coincidencias de cadena2 en cadena e imprime cadena  *
*                                                                        *
**************************************************************************/
#include<stdio.h>
#define TAMANO 100

void squeeze( char s[], char t[],  int c);
void Imprime(char cl[], int n);

/*//////////////////////////////////////
// MAIN
//////////////////////////////////////*/ 

int main()
{  /* Abre main */

char cadena[TAMANO] = "La felicidad esta en lugares mas o menos lejanos," " como Londres";
char cadena2[TAMANO] = "Hola";
/*Las cadenas se pueden concatenar en tiempo de complilacion*/

/*cadena antes de llamar a la funcion squeeze*/
printf("\nCadena 1: \n");
Imprime(cadena, TAMANO);
printf("\nCadena 2:\n");
Imprime(cadena2, TAMANO);

printf("\n");

squeeze (cadena, cadena2, TAMANO);
 
/* Cadena despues de llamar a la funcion squeeze */ 
Imprime(cadena, TAMANO);

printf("\n");

return 0; 
}  /* Cierra main */

/*/////////////////////////////////////
// SQUEEZE
/////////////////////////////////////*/

void squeeze( char s[], char f[], int c)
{  /* Abre squeeze */
int i, j, k;

for (k = 0; k < c; k++) 
{ /*Abre for*/
for (i = j = 0; s[i] != '\0'; i++)
if ( s[i] != f[k]) 
s[j++] = s[i];

s[j] = '\0';
} /*Cierra for*/
}  /* Cierra squeeze*/

/*//////////////////////////////////////
//  IMPRIME
//////////////////////////////////////*/

void Imprime(char cl[], int n)
{  /* Abre Imprime */
for (n = 0; cl[n] != '\0'; n++ )
putchar(cl[n]);

}  /* Cierra Imprime */

La ejecución de éste programa produce la salida:

Cadena 1: 
La felicidad esta en lugares mas o menos lejanos, como Londres
Cadena 2:
Hola
L feicidd est en ugres ms  mens ejns, cm Lndres

que es, efectivamente, la cadena original sin los elementos de la segunda.

__________________________________________________________________________________________
Esta entrada es parte de los problemas resueltos del libro El Lenguaje de Programación C de B. Kernighan y D. Ritchie
Entrada Anterior
Entrada Siguiente
__________________________________________________________________________________________

viernes, 8 de junio de 2012

Aritmética en el Lenguaje de Programación C

Antes de hablar de aritmética en el lenguaje C, vamos a hablar simplemente de la aritmética.
¿Cuál es el resultado de la siguiente operación?

5 + 4 x 6 = ?

Muchas personas responden de inmediato que el resultado es 54, porque si se suma 5 + 4 el resultado es 9; si ahora se multiplica 9 x 6, el resultado es 54. Otras contestan que el resultado es 29, porque 4 x 6 = 24, y 5 + 24 = 29.
Este ejemplo es ilustrativo, porque muestra claramente que en aritmética es importante el orden en el cual los operadores actúan. Esta importancia es más grande todavía en programación. La respuesta correcta es la segunda,

5 + 4 x 6 = 29

La razón de ésto es que por convención, la multiplicación tiene un orden de precedencia mayor que la suma. Las operaciones NO se realizan en el orden de aparición de los operadores. La norma es, primero se realizan las multiplicaciones y divisiones y luego sumas y restas. Cuando en una expresión aritmética sólo aparecen sumas (restas) o sólo multiplicaciones (divisiones), el orden en el que se realicen estas operaciones sí es indiferente. El lector tal vez recuerda estos resultados como las leyes asociativas y conmutativas de la suma(resta) y la multiplicación (división): el orden de los sumandos no altera la suma y el orden de los factores no altera el producto.
Los operadores aritméticos binarios del lenguaje c son

_______________________________________________________________
|OPERADORES ARITMETICOS BINARIOS DEL LENGUAJE DE PROGRAMACION C|
_______________________________________________________________
|SIMBOLO EN C   | EQUIVALENTE MATEMATICO | EJEMPLO             |
________________________________________________________________
|      *        |            x           |  5*7 = 35           |
________________________________________________________________
|      /        |            /           |  15/3 = 5           |
_______________________________________________________________
|      +        |            +           |  7 + 4 = 11         |
________________________________________________________________
|      -        |            -           |  14 - 8 = 6         |
________________________________________________________________
|      %        |            %           |  9%2 = 1            |
_______________________________________________________________


Los operadores aritméticos de suma, resta y división son los mismos en C y en matemáticas. No así el operador de multiplicación, que en C es *. La razón de ésto, es que el lenguaje toma cualquier letra, como x ó X, como nombres de variables y no como operadores. El signo * está reservado para ser el operador de multiplicación. El lector está familiarizado con dichas operaciones, y no será necesario abundar en ellas. Probablemente el operador de módulo % no sea tan familiar. Éste operador recibe dos enteros y devuelve el residuo de la que queda al realizar la división de tales números. En el ejemplo presentado en la tabla de arriba, el cociente (entero) de 9/2 = 4, y el residuo es 1.
Para alterar el orden natural de ejecución de los operadores es necesario usar paréntesis. Si en el caso de la suma presentada al principio, usamos un par de paréntesis como éstos, que rodean al 5 y al 4

(5 + 4) x 6 = 54

obtendremos como resultado 54. Los paréntesis indican que lo contenido dentro de ellos, debe considerarse como un sólo número. Primero deben hacerse pues, las operaciones indicadas ahí, y el resultado final multiplicarlo por 6. Casos más complicados pueden aparecer cuando hay más de un par de paréntesis y algunos de ellos están anidados

((4 - 6) - 7*(2 + (-8)) = ?

4 pares de paréntesis aparecen aquí. El primero, externo, rodea a toda la expresión. Ese podría ser suprimido sin ningún problema. Sin embargo, muchas veces, cuando escriba programas, verá que es útil rodear una expresión aritmética con paréntesis externos. El siguiente par rodea a 4 - 6. Esto quiere decir, como en el caso anterior, que hay que considerar esa expresión como un sólo número; ese número es -2. El siguiente par de paréntesis rodea a 2 + (-8). El otro par de paréntesis rodea a -8. En realidad también es redundante, ya que podría haberse escrito 2 - 8, pero la literal también es válida. El resultado de esa expresión es -6. Así que, suprimiendo los paréntesis internos, la expresión queda como

-2 - 7*(-6) = ?

Aquí sólo aparece un par de paréntesis, rodeando a -6. Primero se realiza la multiplicación, -7*(-6) = 42. -2 + 42 = 40. El resultado es 40.
Para la potenciación, logaritmo y raíz cuadrada, el lenguaje C incluye funciones preestablecidas. Éstas funciones están empacadas en el encabezado <stdio.h>

Sin embargo, hay que tener en cuenta que en C los números reales son almacenados en espacios de memoria, y el resultado de las operaciones aritméticas que con ellos se realizan, también. Las operaciones entre números enteros (int) producen un int; las operaciones entre float produce números float. Sin embargo, no es posible realizar, por ejemplo, la suma de un tipo int con un tipo float. En caso de ocurrir una operación de ese tipo, el compilador convierte el tipo int a tipo float de manera implícita, y el resultado es un float. En general la regla es que los tipos menores se convierten a los tipos mayores, lo cual ocurre sin pérdida de precisión. En general, y a pesar de dicha conversión, no es conveniente permitir que ocurran tales casos. El almacenamiento de un tipo float (producto de una operación aritmética) en un tipo int no es posible, y el intento produce un mensaje de error.
Los operadores en una expresión aritmética podrían ser funciones de variables. Por ejemplo:

int a, s, t;

a = f(s) + g(t)

En este fragmento de programa, a la variable entera a se le asigna la suma de los valores de retorno (tipos int) de las funciones f (que depende del parámetro entero s) y g (dependiente, a su vez, del parámetro entero t). En casos como éste, el estándar de C no indica qué función se evalúa primero. Un compilador, en una computadora podría evaluar primero g(t) y luego f(s), en tanto que otro compilador (o incluso el mismo) en una computadora distinta, puede hacer la evaluación al revés. Esto es especialmente grave si las funciones dependen del mismo parámetro y lo modifican en los cuerpos de instrucciones.
También es necesario cuidar la precedencia cuando uno escribe segmentos de código como

a[i] = i++;

Los operadores de incremento y decremento pueden producir sutiles errores cuando se usa la variable incrementada o decrementada en la misma expresión. En este ejemplo, el estándar no dice qué se hace primero, si incrementar el contador o localizar el espacio de memoria en el arreglo. Como recomendación contra éstos errores, es necesario evitar expresiones en las que el resultado depende el orden de evaluación de los elementos de dichas expresiones.
Vale aquí una digresión que no está del todo fuera de lugar. El procesador de una computadora para fines teóricos es llamado unidad de proceso (PU por sus siglas en inglés). Una PU consta de dos partes: la unidad lógica aritmética (ALU) y la unidad de control (CU). La primera es capaz de realizar sumas, restas, etc. y llevar a cabo operaciones lógicas del tipo igual que, mayor o igual que, etc; en tanto que la segunda coordina la realización de esas tareas aritméticas y lógicas. La ALU sólo puede llevar a cabo una operación a la vez, por lo cual es importante el papel jugado por la CU al asignarle las tareas mediante las instrucciones adecuadas. Estas instrucciones se realizan con un orden determinado y en conjunto se les conoce como ciclo de instrucción. Éstos pasos son:
Buscar y traer (Fetch) La PU busca y trae la siguiente instrucción del programa que está ejecutando.
Decodificar la instrucción LA PU tiene que saber qué tipo de instrucción va a realizar la ALU (suma, multiplicación, división, etc.)
Ejecutar la instrucción Esta es la tarea realizada por la ALU.
Almacenar el resultado El resultado de la operación aritmética o lógica realizada por la ALU es almacenada en memoria para su posterior utilización.
Todas las computadoras realizan este ciclo de instrucción, y todo lo que saben hacer las computadoras es solamente esto. Es sorprendente que audio, video,comunicación remota y todas las tareas para las que sirven las computadoras sean realizadas llevando a cabo sólo operaciones lógicas y aritméticas, pero así es. Tan importante es este ciclo, que los procesadores valen por la cantidad de ciclos de instrucción que pueden hacer en un segundo. Un procesador de 3 GHz realiza 3 x 1000000000 ciclos por segundo.
Ninguna tarea tiene control absoluto del procesador. Todas las tareas se enlistan y se efectúan con un orden que depende de la prioridad que tiene la tarea. Los sistemas operativos se crearon para administrar las tareas que hará el procesador y el tiempo asignado a ellas.

miércoles, 30 de mayo de 2012

Las Funciones Getchar y Putchar

La función getchar recibe un carácter, mientras que la función putchar imprime un carácter. Este par de funciones son más útiles de lo que parece. Permiten manipular de distintas maneras archivos y caracteres. El siguiente programa es un clásico. Recibe carácter por carácter la entrada del usuario (puede ser un número, una cadena, un archivo) con la función getchar y lo imprime con la función putchar.


/*+++++++++++++++++++++++++++++++++++++++++
  * ESTE PROGRAMA LEE E IMPRIME UNA CADENA + 
  * CARACTER POR CARACTER                  +
  * +++++++++++++++++++++++++++++++++++++++*/

 /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  *                                                       +
  *                   ALGORITMO                           +
  * Solicitar una cadena                                  +
  *                                                       +
  + Recibe un caracter                                    +
  * Mientras (el caracter recibido no sea fin de archivo) +
  * { // Abre mientras                                    +
  * Imprime el siguiente caracter                         +
  * Incrementa en uno el numero de caracteres             +
  * Recibe el siguiente caracter                          +
  * }  // Cierra mientras                                 +
  *                                                       +
  *++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

  #include <stdio.h>
  
  int main()
  {  /* Abre main */
  int c;

  printf("\nEste programa recibe e imprime una cadena de caracteres ");
  printf("caracter por caracter.\nPor favor introduzca la cadena: \n");

  while(EOF != (c = getchar()))
  putchar(c);
  
  return 0;
  } /* Cierra main */

_____________________________________________________________________________________________
Ahora hagamos un análisis del programa línea por línea para revisar cada instrucción

/*+++++++++++++++++++++++++++++++++++++++++
  * ESTE PROGRAMA LEE E IMPRIME UNA CADENA + 
  * CARACTER POR CARACTER                  +
  * +++++++++++++++++++++++++++++++++++++++*/

 /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  *                                                       +
  *                   ALGORITMO                           +
  * Solicitar una cadena                                  +
  *                                                       +
  + Recibe un caracter                                    +
  * Mientras (el caracter recibido no sea fin de archivo) +
  * { // Abre mientras                                    +
  * Imprime el siguiente caracter                         +
  * Incrementa en uno el numero de caracteres             +
  * Recibe el siguiente caracter                          +
  * }  // Cierra mientras                                 +
  *                                                       +
  *++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

Este par de comentarios indican de qué va el programa.

#include <stdio.h>

Un encabezado que indica que se usará entrada y salida estándar. Además de eso, en este programa se hace uso de otro contenido de este encabezado: la definición del valor del carácter que indica el fin de archivo EOF (End of File)

int main()

Recuerde que en C la unidad básica de programación es la función. El nombre main es también un buen nombre para esta función. main se llamaba la función principal en el lenguaje de programación B, del cual evolucionó el lenguaje C.

{  /* Abre main */

Los corchetes se utilizan en cualquier parte donde pueda ponerse una instrucción simple (salvo en el operador ternario). En el caso de la función main (en realidad en cualquier definición de función) las llaves son obligatorias. NO es correcto escribir un programa como éste:

int main()

return 0;

Un programa con una función main vacía debe escribirse así

main()
{

}

Éste es el programa más corto que puede escribirse en el lenguaje C. Sólo consta de 8 caracteres. Sobra decirlo: el programa no hace nada, pero sintácticamente está bien. Es prescindible el encabezado stdio.h, la instrucción return y el tipo int. No son prescindibles el nombre main, los paréntesis después del nombre, ni las llaves. Todos los cuerpos de las funciones deben estar contenidos en llaves.

int c;

Esta declaración de variable reserva espacio para un tipo entero. La variable, c, será utilizada para almacenar temporalmente cada uno de los caracteres. El tipo de datos char es el tipo adecuado para almacenar caracteres. ¿Por qué se utiliza entonces el tipo int? Porque c también debe guardar el valor de EOF, el fin de archivo, el cual generalmente es -1 (véase este programa). Dado que los tipos char son enteros sin signo, al declarar a la variable c como tipo char, no se podría distinguir el fin de archivo.

printf("\nEste programa recibe e imprime una cadena de caracteres ");
printf("caracter por caracter.\nPor favor introduzca la cadena: \n");

Este par de líneas printf son un mensaje para el usuario.

while(EOF != (c = getchar()))

Esta es una forma muy compacta de escribir código. Dentro de la condición de while ocurren varias cosas, y ellas tienen que ver con el orden de precedencia de los operadores. Lo que está dentro de los paréntesis externos, sea lo que sea; se evalúa como una expresión booleana. Si no es una expresión booleana, entonces se convierte explícitamente a una, esto quiere decir que si el valor de dicha expresión es 0, entonces se toma como "falso" y no se realiza el cuerpo de if y por otra parte, el compilador tomará como "verdadero" todo valor distinto de 0, y en ese caso se ejecuta el cuerpo de la instrucción while. Lo primero que "ve" el compilador dentro de los paréntesis externos es una condición:
EOF != (expresion) ¿Qué es EOF? EOF es el caracter que indica el fin del archivo. En los sistemas Unix el fin de archivo es Ctrl D, en tanto que en los sistemas windows es Ctrl C. Si usa un sistema distinto, deberá averiguar cuál es la combinación de teclas que indican EOF. EOF tiene un valor determinado por la plataforma en la que programe. Este valor tiene que ser tal que no se pueda confundir con cualquier carácter válido (como a, b, c, \t, etc). Generalmente se trata de un número negativo (-1). La condición dentro de los paréntesis de while dice "mientras el carácter leído sea distinto de EOF, realiza este cuerpo de instrucciones". Pero ¿qué hay a la derecha del operador relacional !=? Este operador es un operador "izquierdo", toma el valor de la izquierda y lo compara con el de la derecha. A su derecha, y entre paréntesis aparece la expresión x = getchar() ¿Por qué aparece esta expresión entre paréntesis? Porque la precedencia del operador relacional != es superior a la del operador de asignación =. De no haber paréntesis, el compilador primero compararía el valor de c con EOF y luego trataría de asignar el valor de getchar() a la cantidad que se encuentra a la izquierda de =, lo cual provocará un error en la compilación; en este caso los paréntesis son necesarios. Por lo tanto, después de evaluar el valor de EOF, el compilador evalúa la expresión c = getchar(), y aquí se encuentra una expresión que contiene al operador = de asignación. La asociatividad de = es de derecha a izquierda, primero se evalúa la expresión a la derecha y eso se le asigna a la variable c. Pero la expresión de la derecha es una invocación de la función getchar(), definida en el encabezado stdio.h Esa función lo que hace es recibir un carácter de la entrada. La sintaxis general de la función getchar es la siguiente:

int getchar(void)

La función retorna un entero (el valor del carácter leído) y no recibe ningún parámetro. Este valor entero se asigna a la variable c, que se ha declarado previamente como tipo int. Y es ese valor, el de c, el que se compara con EOF. Si el carácter leído es distinto a EOF, entonces se lee el siguiente y así. Se trata pues de un ciclo while controlado por centinela.

putchar(c);

El cuerpo de while consta de una sola instrucción: la función putchar, la cual recibe un entero y retorna también un entero. Esos int son los valores de un carácter. La forma general de la función putchar es la siguiente:

int putchar(int caracter);

En este programa, putchar(c) simplemente envía a la salida estándar el carácter que getchar() acaba de leer.

return 0;

La instrucción return 0; como en todos los programas, indica que se ha terminado el programa satisfactoriamente.

} /* Cierra main */

Esta llave indica el fin del cuerpo de main. Muchas veces le va a ocurrir que hay cuerpos de instrucciones que contienen llaves sin cerrar. Para evitar esto, créese el buen hábito de cerrar el cuerpo antes de escribir las instrucciones. También acostúmbrese a indicar, en cada caso, a qué instrucción corresponden las llaves.
_______________________________________________________________________________________________
Lo que aprendió:
El significado de EOF
La función getchar()
La función putchar()
_______________________________________________________________________________________________
Esta entrada forma parte del Curso de C con Programas Explicados Línea por Línea
Índice
Entrada Anterior
Entrada Siguiente
Related Posts Plugin for WordPress, Blogger...