Mostrando entradas con la etiqueta Kernighan-Ritchie. Mostrar todas las entradas
Mostrando entradas con la etiqueta Kernighan-Ritchie. Mostrar todas las entradas

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

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, 22 de julio de 2011

Kernighan_Ritchie_2.3 (Convertir de Hexadecimal a Decimal en C)

___________________________________________________________________________________
2.3 Escriba la función htoi(s), que convierte una cadena de dígitos hexadecimales (incluyendo 0x ó 0X en forma optativa) en su valor entero equivalente. Los dígitos permitidos son del 0 al 9, de la a a la f, y de la A a la F.
___________________________________________________________________________________
Solución: Este programa convierte de hexadecimal a decimal. El programa está bien comentado y creo que es fácil de seguir y entender. Es posible realizar éste problema de otras maneras, ésta es una de ellas. El usuario puede introducir los numeros 0, 1, ....9 y las literales A, a, B, b,.....F, f. El uso de minúsculas hace el programa más largo. Cualquier otro dígito envía un mensaje de error. Tal vez le convenga leer la entrada Los números hexadecimales. Una tabla de equivalencias entre números binarios, octales, decimales y hexadecimales, aparece en Ésta página.



/*Este programa convierte de hexadecimal
   a decimal */

 #include <stdio.h>

 #define Tamano 35
 // La variable Tamano se fija en 35
 // porque los enteros de dos bytes
 // ocupan 32 bits y la ultima entrada
 // de una cadena en un arreglo es \n

 /*Prototipo de funcion Recibe*/
 /* Esta funcion recibe la cadena
    pero no verifica que sea valida */

 void Recibe( int a[]);

 /*Prototipo de funcion Imprime*/
 /* Si la cadena es valida se calcula
 e imprime su equivalente decimal, si no
 se manda un mensaje al usuario y se termina
 el programa */

 void Imprime( int b[]);

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

 int main()

 {       // Abre main 

 int arreglo[Tamano];
 // En este arreglo se recibiran los valores
 // de la cadena hexadecimal
 int evaluar;
 // La variable evaluar lleva el control
 // de la legalidad de la cadena, es decir
 // se introducen digitos validos como e, 1
 // o se introducen valores invalidos como
 // s, z

 // Un  mensaje que avisa al usuario que
 // hace el programa
 printf("\nEste programa recibe un numero "     
        "hexadecimal y lo convierte a decimal.\n");

 // Se invoca a la funcion Recibe y se le envia
 // la direccion de la primera posicion de arreglo

 Recibe(arreglo);

 // Se calcula e imprime el valor decimal, de 
 // nuevo por referencia 

 Imprime(arreglo);

 return 0;

 }       // Cierra main

 /*////////////////////////////////////
 // FUNCION RECIBE
 ////////////////////////////////////*/

 void Recibe( int a[])

 {  // Abre Recibe 

 /* En el arreglo a se guardaran las 
   entradas de la cadena*/

 int i = 0;
 /* La variable i sirve como contador
    en varios ciclos */

 for( i = 0; i < Tamano; i++ )
 a[i] = ' ';
 // Las entradas se inicializan a ' ' 

 printf("\nIntroduzca un numero hexadecimal: \n");
 /*El siguiente while recibe la cadena y la
   asigna al arreglo en una sola linea.
   Observe que al final hay un ;  */

 i = 0;
 // Se pone i = 0

 while ( (a[i++] = getchar()) != '\n' && Tamano > i );
 printf("\n");

 }  // Cierra Recibe 

 /*/////////////////////////////////////
 // FUNCION IMPRIME
 /////////////////////////////////////*/

 void Imprime( int b[] )

 {  // Abre Imprime

 int i;
 int potencia = 1;
 // La variable potencia lleva el valor de la
 // potencia de 16 por la cual se va a multiplicar
 // el digito almacenado dependiendo de su posicion
 int decimal = 0;
 // El equivalente decimal de la cadena hexadecimal
 // se inicia a 0
 int inicio;
 // La variable inicio se encarga de registrar
 // en donde empieza el primer digito de la cadena.
 // Esta cadena debe leerse de izquierda a derecha.

 // Este ciclo for busca de atras para 
 // adelante el primer digito distinto
 // de ' ' en el arreglo. A partir de
 // ahi se empieza a evaluar el numero
 // hexadecimal

 for ( i = Tamano - 1; i >= 0; i-- )

 {  // Abre for
 if ( ' ' != b[i] )
 {  // abre if  
 inicio = i;
 break; // sale de for
 }   // Cierra if
 }  // Cierra for

 // Este ciclo for evalua cada una de las
 // entradas del arreglo que fueron introducidas 
 // por el usuario, las multiplica por la potencia 
 // de 16 correspondiente a su posicion y los
 // productos los va sumando. La suma total sera
 // el valor decimal del numero. 

 int valido = 1;
 // Si el hexadecimal recibido no es valido, no 
 // se puede calcular su valor decimal. Esta 
 // variable controla eso. En principio se establece
 // en 1 o cierto, ya que se asume que el usuario 
 // introdujo un numero valido

  for ( i = inicio; i >= 0; i-- )
 {  // Abre for
 switch( b[i] )
 {  // Abre switch
 case '0':
 decimal += 0*potencia;
 potencia *= 16;
 break;
 case '1':
 decimal += 1*potencia;
 potencia *= 16;
 break;
 case '2':
 decimal += 2*potencia;
 potencia *= 16;
 break;
 case '3':
 decimal += 3*potencia;
 potencia *= 16;
 break;
 case '4':
 decimal += 4*potencia;
 potencia *= 16;
 break;
 case '5':
 decimal += 5*potencia;
 potencia *= 16;
 break;
 case '6':
 decimal += 6*potencia;
 potencia *= 16;
 break;
 case '7':
 decimal += 7*potencia;
 potencia *= 16;
 break;
 case '8':
 decimal += 8*potencia;
 potencia *= 16;
 break;
 case '9':
 decimal += 9*potencia;
 potencia *= 16;
 break;
 case 'A':
 decimal +=  10*potencia;
 potencia *= 16;
 break;
 case 'a':
 decimal +=  10*potencia;
 potencia *= 16;
 break;
 case 'B':
 decimal +=  11*potencia;
 potencia *= 16;
 break;
 case 'b':
 decimal +=  11*potencia;
 potencia *= 16;
 break;
 case 'C':
 decimal +=  12*potencia;
 potencia *= 16;
 break;
 case 'c':
 decimal +=  12*potencia;
 potencia *= 16;
 break;
 case 'D':
 decimal +=  13*potencia;
 potencia *= 16;
 break;
 case 'd':
 decimal +=  13*potencia;
 potencia *= 16;
 break;
 case 'E':
 decimal +=  14*potencia;
 potencia *= 16;
 break;
 case 'e':
 decimal +=  14*potencia;
 potencia *= 16;
 break;
 case 'F':
 decimal +=  15*potencia;
 potencia *= 16;
 break;
 case 'f':
 decimal +=  15*potencia;
 potencia *= 16;
 break;
 default:
 if ( '\n' != b[i])
 {  // Abre if
 printf("\nERROR. LA CADENA NO ES VALIDA!\n");
 valido = 0;  // El hexadecimal recibido no 
              // es valido
 printf("\nEste caracter no es valido: ");
 putchar(b[i]);
 printf("\n");
 } // Cierra if
 break;
 }   // Cierra switch
 }  // Cierra for  

 if ( 1 == valido )
 printf("\nEl valor decimal es: %d\n", decimal);
 else
 printf("\nEl numero introducido no es valido.\n");

 }  // Cierra Imprime
__________________________________________________________________________________________
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, 20 de enero de 2011

Kernighan_Ritchie_2.1 (Valores Máximos y Mínimos de los Tipos de Datos en C)

Ejercicio 2.1 Escriba un programa que imprima los valores máximos y mínimos de los tipos de datos int, char, long, etc. de su computadora.

El lector está acostumbrado a los números enteros y reales de las matemáticas elementales. Almacenar cada uno de esos tipos en el lenguaje C requiere más o menos espacio de memoria dependiendo del tamaño de los números. Por ejemplo, representar el número 3,14159265358979323846 requiere más espacio que el almacenamiento de 20 ó -123. Para optimizar la memoria debe asignarse un tipo de dato a cada número dependiendo de su tamaño. En atención a esto tenemos los siguientes datos en C:

Los  tipos enteros:                         Memoria que los contiene
        char                                                 |-|-|
        short                                                |-|-|-|-|
        int                                                    |-|-|-|-|-|-|-|-|
        long                                                 |-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|
                                                       

Los tipos flotantes:
       float
       double

Los tipos de enumeración:
       enum

Los tipos de dirección:
       Apuntador
       Referencia

Los tipos estructurados:
      struct
      union
      Arreglos

Los tipos enteros guardan sólo números enteros, en tanto que los números float y double sirven para almacenar decimales.
Los tamaños relativos para los tipos de datos enteros son representados esquemáticamente en la tabla por el número de elementos de memoria. Esto no se debe de tomar tan en serio puesto que a fin de cuentas el tamaño de esos números depende de la plataforma en que se ejecute el programa (procesador, sistema operativo, compilador).  Como norma el estandar dice que los tipos short e int deben ser al menos de 16 bits y long de 32. Además short no es mayor que int, el cual a su vez no es mayor que long. A estos tipos se les puede aplicar los calificativos signed o unsigned. Siendo los unsigned números enteros no negativos. Conforme vayan apareciendo los tipos de datos se irán analizando cada uno de ellos. Por lo pronto para verificar los  valores máximos y mínimos de su computadora debe copiar, pegar, compilar y ejecutar el siguiente programa.


/* Este programa imprime los tipos maximos y minimos para los tipos int, char,
long, etc, en una computadora particular. Las constantes estan definidas en el
encabezado <limits.h> que en el libro de Kernighan y Ritchie se encuentra
descrito en el apendice B */ 

#include <stdio.h>
#include <limits.h>

int main()
{ // Abre main

printf("\nEl valor minimo del tipo char en esta computadora es: %d\n", CHAR_MIN);
printf("\nEl valor maximo del tipo char en esta computadora es: %d\n", CHAR_MAX);
printf("\nEl valor minimo del tipo int en esta computadora es: %d\n", INT_MIN);
printf("\nEl valor maximo del tipo int en esta computadora es: %d\n", INT_MAX);
printf("\nEl valor minimo del tipo long en esta computadora es: %d\n", LONG_MIN);
printf("\nEl valor maximo del tipo long en esta computadora es: %d\n", LONG_MAX);
printf("\nEl valor maximo del tipo unsigned int en esta computadora es: %d\n", UINT_MAX);

return 0;
} // Cierra main
__________________________________________________________________________________ 
Ahora hagamos un análisis del programa línea por línea para revisar cada instrucción.
/* Este programa imprime los tipos maximos y minimos para los tipos int, char,
long, etc, en una computadora particular. Las constantes estan definidas en el
encabezado <limits.h> que en el libro de Kernighan y Ritchie se encuentra
descrito en el apendice B */ 
Estas tres líneas son un comentario con información general sobre el programa. Recuerde que los comentarios en C empiezan con /* y terminan con */ Recuerde también que es una buena idea comentar los programas. Este comentario en particular hace referencia a un clásico de la programación en C: El Lenguage de Programación C, un libro de B. Kernighan y Dennis Ritchie, creadores del lenguaje.
#include <stdio.h>
Para incluir un encabezado cualquiera debe hacer uso de #include. A continuación debe utilizar el nombre del encabezado necesario. stdio.h incluye varias funciones y utilidades que serán necesarias. Siempre que use entrada o salida de datos debe incluirlo.
#include <limits.h>
Los tamaños máximos y mínimos de los tipos de datos en cualquier plataforma particular están definidos en este encabezado mediante las constantes CHAR_MIN, CHAR_MAX, etc.
int main()
{ // Abre main
La función main aparece en todos los programas. Se trata, como su nombre en inglés lo dice, de la función principal. Todas las funciones reciben y entregan datos. int es el tipo de datos que main entrega. Los paréntesis vacíos indican que la función main no recibe ningún dato. El cuerpo de main, como el de todas las funciones, está delimitado por llaves { } La forma de comentar que aparece aquí es nueva. El compilador pasa por alto todo lo que se encuentra después de //, estos caracteres inclusive. El comentario termina con el primer salto de línea. A pesar de que la forma estándar de comentar es usando /* */ practicamente todos los compiladores son capaces de reconocer comentarios con // (una forma de comentario propia del lenguaje C++)
printf("\nEl valor minimo del tipo char en esta computadora es: %d\n", CHAR_MIN);
printf("\nEl valor maximo del tipo char en esta computadora es: %d\n", CHAR_MAX); printf("\nEl valor minimo del tipo int en esta computadora es: %d\n", INT_MIN); printf("\nEl valor maximo del tipo int en esta computadora es: %d\n", INT_MAX); printf("\nEl valor minimo del tipo long en esta computadora es: %d\n", LONG_MIN); printf("\nEl valor maximo del tipo long en esta computadora es: %d\n", LONG_MAX); printf("\nEl valor maximo del tipo unsigned int en esta computadora es: %d\n", UINT_MAX);
Estas líneas hacen uso de la función printf para imprimir los máximos y mínimos valores de algunos tipos de datos. Como hemos visto, la secuencia de escape \n indica nueva línea. Esto es, el cursor se coloca al inicio del siguiente renglón. Después de la instrucción printf todo lo que viene a continuación está contenido entre paréntesis, y termina con punto y coma. La estructura es printf( ); Todo lo que se coloque dentro de los paréntesis y entre comillas dobles se imprimirà, salvo que sea una secuencia de escape o una referencia a una variable. Para imprimir enteros es necesario usar %d entre las comillas, después de las comillas es necesario colocar una coma y la variable entera (o variables enteras separadas por comas).
return 0;
La función main retorna un entero. El entero que retorna es 0. Esta instrucción debe ir siempre al final del programa. Indica que se han terminado las instrucciones.
} // Cierra main
Con el cierre de las llaves se termina el cuerpo de main. __________________________________________________________________________________ 
Lo que aprendió: 
Los tipos de datos en C 
Los valores máximos y mínimos de cada tipo básico en su plataforma.
Los comentarios usando //
____________________________________________________________________________________________
Esta entrada forma parte del Curso de C con Programas Explicados Línea por Línea
Índice
Entrada Anterior
Entrada Siguiente

martes, 4 de enero de 2011

Kernighan_Ritchie_1.24

1.24 Escriba un programa para revisar los errores de sintaxis rudimentarios de un programa en C, como paréntesis, llaves y corchetes no alineados. No olvide las comillas ni los apóstrofos, las secuencias de escape y los comentarios. (Este programa es difícil si se hace completamente general.)


     /* Este programa verifica que las entradas de  un programa  no contengan
    errores de sintaxis elementales, como verificar que las llaves,
    corchetes y parentesis se cierren correctamente
    */

    // Definicion de constantes simbolicas y encabezados

   # define OK 0
   # define ERROR -1
   # define FALSO 0
   # define VERDADERO 1
   # define LLENA 1
   # define PILAMAX 100
   # define ADENTRO 1
   # define AFUERA 0
   # include <stdio.h>

   int superior = -1;   // Un par de variables externas

   int pila[PILAMAX];

   empujar( int nuevo )

   // La funcion empujar tiene mete elementos en la pila

   {              // abre la funcion empujar


   if ( superior + 1 >= PILAMAX )

   return( LLENA);


   pila[ ++superior ] = nuevo;

   return( OK);


   }             // Cierra la funcion empujar


   int extraer()
   {                // La funcion extrae  elementos de la pila

   return( pila[superior--] );

   }                // Cierra la funcion extraer


   int vacio()


  {              // Abre vacio

  if( superior < 0 )
  return(VERDADERO);

  return(FALSO);

  }                  // Cierra vacio


   // Aqui se define la funcion a la que se hara referencia
   // Esta funcion realiza la tarea fundamental de revisar que los
   // parentesis se cierren adecuadamente

   int Revisa_Parentesis( int datos[])

   {     // ABre la funcion Revisa_Parentesis

   int i;
   int comentario = AFUERA;

   /* Esta variable se usa para tener en cuenta siempre si se esta o no dentro de un
    comentario, en caso de estarlo, no debe realizarse nada mas que seguir leyendo
    los caracteres hasta que se encuentre la secuencia de terminacion de comentario
    De entrada se empieza fuera de un comentario */

   for ( i = 0; datos[i] !=  0; i++ )

     // En C++ si es posible declarar la variabl int en la primera parte
     // de for; en C la declaracion no puede hacerse en el ciclo

    // Este ciclo for pasa por alto los caracteres que no sean parentesis
    // Los parentesis de apertura se guardan en una pila


    {                 // ABre for

     switch( datos[i])

     {              // ABre switch

     case '{':

     case '[':

     case '(':



     if ( AFUERA == comentario )

     {
     empujar( datos[i]);

     break;

     }

     case '}':

     if ( AFUERA == comentario )

     {

     if (vacio() || extraer() != '{')

     return (ERROR);
     break;

     }

     case ']':

     if ( AFUERA == comentario )

     {

     if (vacio() || extraer() != '[')

     return (ERROR);
     break;

     }

     case ')':

     if ( AFUERA == comentario )

     {

     if (vacio() || extraer() != '(')

     return (ERROR);
     break;

     }


     case '/':

     if ( AFUERA == comentario )

     {

     if ( '*' == datos[i + 1]  )

     comentario = ADENTRO;

     }

     if (ADENTRO == comentario)

     if ( '*' == datos[ i - 1])

     comentario = AFUERA;

     break;

     }             // Cierra switch

    }                    // Cierra for

    if ( ADENTRO == comentario )

    // Es decir, si nunca se cerro un inicio de comentario, entonces es un errror

    return (ERROR);

    if ( vacio())

    return (OK);

    return (ERROR);

   }          // Cierra la funcion Revisa_Parentesis


   int main()

   {             // Abre main


   int cadena[PILAMAX];

   int c;
   int x = 0;

   int j;
   
   printf("\nEste programa funciona como una especie de preprocesador. ");
   printf("\nPor favor introduzca una cadena con comentarios y le dire si ");
   printf(" los parentesis, corchetes, llaves o comentarios estan bien cerrados o no.\n");
   
   
   while( (c = getchar()) != EOF )

   cadena[ x++ ] = c;

   j = Revisa_Parentesis( cadena);

   if ( OK == j  )
   printf("\nLos parentesis estan bien escritos.\n");

   else

   if( ERROR == j )

   printf("\nLos parentesis estan mal escritos.\n");

   }               // Cierra main


 /* Defectos del programa: En realidad el programa incluye varias partes,
 las cuales no pueden ser tratadas de igual manera. Lo mas sencillo es revisar
 si los parentesis (, [, { se cierran de manera adecuada. La manera de hacerlo
 es un ejemplo tipico del poder de las pilas, este programa incluye partes de los
 ejemplos que vienen en el libro de Bowman ( Algoritmos y Estructuras de D    atos.
 Una Aproximacion en C) ademas la parte principal ( la funcion Revisa_Parentesis)
 es una modificacion de la que se presenta en ese libro. El siguiente punto es
 la revision de los comentarios. Para eso se introdujo la variable comentario,
 que indica si se esta o no dentro de uno. Si se esta entonces no vale apilar
 parentesis ni nada mas, lo unico importante es ver si en algun momento aparece
 la condicion de cierre. Se ha utilizado solamente la forma de /* porque es la
 tipica de el lenguaje C, sin embargo, deberia tambien incluirse el caso de //,
 el cual es la forma de comentario en C++ pero que tambien es valido en C,
 para este caso la condicion de
 terminacion es '\n'. Este programa no considera el caso de las comillas ni los
 apostrofos ni las secuencias de escape. Las secuencias de escape tienen validez
 dentro de una cadena, esto, por lo menos en la forma en que se me ocurre,implica
 crear una variable cadena a la que se le asigne ADENTRO o AFUERA. Sin embargo,
 no es posible apilar las comillas porque en cualquier punto el programa no sabe
 si esta es de apertura o de cierre, hay que atender ese problema con otra
 variable. Si se esta dentro de una cadena, es necesario atender las entradas \
 y ver que siga alguna secuencia de escape. El problema es mas o menos el mismo
 para los apostrofos. En el caso de un compilador, este debe ademas indicar la
 linea y el caracter en el que aparece el problema, ademas de decir exactamente
 cual de todas las secuencias no se cerro de manera adecuada, para esto tambien
 hay que introducir nuevas variables.....

 Solo se me ocurre decir, con Kernighan y Ritchie: "Este programa es dificil si
 se hace completamente general"
 */

Related Posts Plugin for WordPress, Blogger...