Sie sind auf Seite 1von 59

76312839.

doc

Pgina 1 de 59

HVF

INTRODUCCIN AL LENGUAGE C
Para iniciar este curso de programacin es necesario establecer unas cuantas bases tiles que se aplicarn a lo largo de todos los temas tratados. En primer lugar veamos como nombrar un identificador, ste es utilizado por cualquier variable, funcin, definicin de datos, etc. En C, un identificador es una combinacin de caractres siendo el primero una letra del alfabeto o un smbolo de subrayado y el resto cualquier letra del alfabeto, cualquier dgito numrico o smbolo de subrayado. Dos reglas debemos tener en mente cuando nombramos identificadores: 1. El tamao de los caracteres alfabticos es importante. Usar PRINCIPAL para el nombre de una variable no es lo mismo que usar principal como tampoco es lo mismo que usar PrInCiPaL. Los tres se refieren a variables diferentes. 2. De acuerdo al estndar ANSI-C, al darle nombre a un identificador solo sern significativos los primeros 31 caractres, todo carcter mas all de este lmite ser ignorado por cualquier compilador que cumpla la norma ANSI-C Un elemento importante es el smbolo de subrayado que puede utilizarse como parte del nombre de una variable, contribuyendo notablemente a la legibilidad del cdigo resultante. Es utilizado por algunos, pero no por todos los programadores en C experimentados. Algunos subrayados sern utilizados en esta tutorial a manera de ilustracin. Debido a que una gran parte de los escritores de compiladores utilizan el subrayado como primer carcter para los nombres de variables internas de sistema, es aconsejable evitar el uso del subrayado para iniciar un identificador para evitar la posibilidad de una confusin de nombres en la etapa de compilacin, ms especfico, los identificadores con doble subrayado estn reservados para uso del compilador as como los identificadores que empiezan con un subrayado seguido de una letra mayscula. Esto es importante. La legibilidad de un programa se incrementa notablemente al utilizar nombres descriptivos para las variables y esto puede ser ventajoso para Usted. Programadores de Pascal y Ada tienden a utilizar nombres descriptivos largos, pero la mayora de los programadores de C tienden a usar nombres cortos y crpticos. Por esta razn la mayora de los programas de ejemplo de este tutorial utilizan nombres muy cortos, pero se usan algunos nombres largos a manera de ilustracin. Sin embargo insistimos en la importancia de utilizar nombres descriptivos que a su vez eviten comentarios redundantes

Palabras clave
Existen 32 palabras definidas como palabras clave en C. Estas tienen usos predefinidos y no pueden ser utilizadas para ningn otro propsito en un programa en C. Estas son utilizadas exclusivamente por el compilador. Afortunadamente para los programadores en espaol, las palabras clave estn definidas en Ingls por lo que resulta til al momento de evitar confusin con los identificadores que utilicemos para nuestros programas, adems siempre estn escritas en letra minscula, la lista completa es la siguiente: auto double int struct break else long Switch case enum register typedef char extern return Union const float short unsigned continue for signed Void default goto sizeof volatile do if static While Adicionalmente su compilador puede definir algunas palabras clave, mismas que estarn enlistadas en la documentacin del mismo. Cada una de las palabras clave arriba mencionadas sern definidas, ilustradas y utilizadas a lo largo de este curso.

Datos y programa.
Todo programa de computadora tiene dos entidades a considerar: los datos, y el programa en s. Estos son altamente dependientes uno del otro y una cuidadosa planeacin de ambos conducir a un programa bien escrito. Desgraciadamente no es posible estudiar cualquiera de estas sin un conocimiento en la otra parte, por esta razn este curso tratar de mostrar tanto mtodos de escritura de programas como mtodos de definicin de datos. Simplemente siga adelante y Usted tendr un buen conocimiento de ambos. Conforme avance por los programas de ejemplo encontrar que cada uno est completo, por lo que no hay fragmentos que resulten confusos, esto le permitir ver cada requerimiento necesario para utilizar cualquiera de las caractersticas de C conforme se vayan presentando. A lo largo de este curso, las palabras clave, los nombres de variables y los nombres de funciones estarn escritas en negrita y todas ellas sern completamente definidas a lo largo de este curso. Cada cdigo presentado en este tutorial ha sido probado utilizando Symantec C++ version 7.5. El resultado de la ejecucin de cada programa lo mostramos con una imagen capturada directamente del monitor al momento en que probamos el respectivo cdigo, en otros casos mostraremos el resultado en forma de comentario al final del cdigo fuente una vez que demos la definicin de comentario mas adelante. Si Usted piensa que entiende completamente el programa, puede consultar simplemente el resultado de la ejecucin, en este caso no es necesario compilar y ejecutar cada programa, sin embargo es aconsejable la compilacin de algunos de los programas debido a que diferentes compiladores no producen exactamente los mismos resultados y es necesario que Usted se familiarice con su propio compilador. Adems es posible seleccionar el cdigo directamente del navegador, copiarlo y pegarlo en el editor de texto del compilador que Usted utilice. Para probar que su compilador C est funcionando adecuadamente compile y ejecute el siguiente programa:

76312839.doc

Pgina 2 de 59

HVF

# include &ltstdio.h> int main () { int index ; for (index = 0; index = 7; index = index + 1) printf ("Primer programa de ejemplo.\n") ; return 0 ; } No se preocupe si no entiende que hace este programa, a su debido tiempo lo entender completamente. Volver al principio

ELEMENTOS BASICOS DE UN PROGRAMA EN C.


El siguiente cdigo representa el programa mas simple en C: main () { } La palabra main es muy importante y debe aparecer solo una vez en todo programa C. Este es el punto donde inicia la ejecucin del programa. Posteriormente veremos que no necesariamente debe ser el primer enunciado del cdigo. Siguiendo a la palabra main esta un par de parntesis que le indican al compilador la existencia de una funcin, la explicacin de qu es una funcin la veremos a su debido tiempo, por lo pronto es recomendable simplemente incluir los parntesis. Las llaves que siguen en las lneas 2 y 3 se utilizan para definir los lmites del programa. Los diferentes enunciados del programa van colocados dentro de estas llaves. El cdigo que actualmente estamos estudiando representa un programa que no hace absolutamente nada y por lo tanto no tiene ningn enunciado ejecutable, sin embargo es posible compilar y correr este programa, lo importante es que se trata de un programa C vlido. Veamos ahora un cdigo mas interesante: #include &ltstdio.h> int main() { printf ("Esta es una lnea de texto."); return 0; } Este cdigo incluye un enunciado ejecutable adems del enunciado obligatorio return. El enunciado ejecutable es una llamada a una funcin incluida como parte de su librera C, la funcin se llama printf ( ) y esta definida en el archivo de cabecera stdio.h. Esta funcin despliega texto en el monitor, para tal fin es necesario colocar la cadena de texto que deseamos mostrar en el monitor entre comillas y dentro del parntesis que sigue a la palabra printf. Observe que al final del enunciado se ha puesto el smbolo de punto y coma. C utiliza el punto y coma para indicarle al compilador que una lnea ejecutable est completa. Al compilar y correr este programa usted ver en la pantalla del monitor la cadena de texto especificada. Los enunciados de las lneas 1 y 6 as como el uso de la palabra int sern explicados mas adelante, mientras es importante ver algunos conceptos bsicos adicionales que le presento en el siguiente cdigo: #include &ltstdio.h> int main () { printf ("Esta es una lnea de texto.\n"); printf ("Y esta es otra "); printf ("lnea de texto.\n\n"); printf ("Esta es una tercera lnea.\n"); return 0; } Observe que ahora estn incluidos cuatro enunciados ejecutables cada uno iniciando con una llamada a la funcin printf ( ), la lnea superior ser ejecutada en primer lugar seguidas de las otras tres lneas ejecutables en el orden en que aparecen, note el carcter cercano al fin de la primera lnea ejecutable, la diagonal invertida ( \ ) conocida en ingls como backslash, es utilizada en la funcin printf ( ) para indicar que sigue un carcter especial de control. En este caso, la "n" indica la peticin de una nueva lnea de texto, es una indicacin para regresar el cursor al lado izquierdo del monitor y a la vez moverlo una lnea abajo. Usted puede colocar un carcter "n" en cualquier parte del texto impreso e iniciar una nueva lnea, incluso en la mitad de una palabra y de esta

76312839.doc

Pgina 3 de 59

HVF

manera dividir la palabra entre dos lneas. Ahora es posible una descripcin detallada del programa. La primer printf ( ) despliega una lnea de texto y regresa el cursor. La segunda printf ( ) despliega otra lnea de texto pero sin regresar el cursor de tal manera que la tercera lnea aparece al final de la segunda, entonces le siguen dos retornos de cursor dando como resultado un espacio en blanco. Finalmente la cuarta instruccin printf ( ) despliega una nueva lnea de texto seguida por el retorno del cursor finalizando el programa. Esta sera la salida mostrada en su monitor: Es buena idea experimentar con este programa agregando instrucciones printf ( ) para asegurarnos de entender como trabaja esta funcin, cuanto mas modifique y compile los ejemplos dados en este tutorial tanto ms aprender conforme avance en su trabajo.

VISUALIZACION NUMERICA.
Este es el cdigo que utilizaremos como primer ejemplo de cmo trabajar con datos en un programa C: # include &ltstdio.h> int main() { int indice; indice = 13; printf("El valor de indice es %d\n", indice); indice = 27; printf("El valor de indice es %d\n", indice); indice = 10; printf("El valor de indice es %d\n", indice); return 0; } El punto de entrada main ( ) debe resultarle claro as como la primera llave. Lo nuevo que encontramos est en la lnea 4 que nos dice int indice; la cual se utiliza para definir una variable de tipo entero llamada indice. La palabra int es una palabra clave de C y no puede ser utilizada con otros fines, define una variable que almacena un nmero entero dentro de un rango predefinido de valores, definiremos el actual rango posteriormente. El nombre de la variable, indice, puede ser cualquier nombre que siga las reglas dadas para un identificador. El punto y coma al final de la lnea es un terminador de enunciado como se explic al principio. Observe que, aunque hemos definido una variable no le asignamos a sta un valor por lo que se dice que contiene un valor indefinido, posteriormente veremos cmo definir varias variables en la misma lnea de instrucciones. Veamos el cuerpo principal del programa, notar que hay tres enunciados que asignan un valor a la variable indice, pero solo uno a la vez. El enunciado en la lnea 5 asigna a indice el valor de 13, y este valor es desplegado en la lnea 6. Despus se asigna a indice el valor de 27, y finalmente le asignamos el valor de 10. Esta claro que indice es una variable que puede almacenar muchos valores diferentes pero solo uno a la vez. El programa una vez compilado aparece de la siguiente forma: Continuando con el analisis del programa veamos los enunciados que contienen printf ( ). Todos son idnticos e inician de la misma forma que los printf ( ) que habamos visto anteriormente, la primera diferencia la encontramos en el carcter %, este seala a la rutina de salida para detener el despliegue de caracteres y hacer algo diferente, generalmente mostrar el valor de una variable. El smbolo % se utiliza para sealar el despliegue de muchos tipos diferentes de variables, pero nos concentraremos en uno solo en este ejemplo. El carcter que le sigue al smbolo % es una d, la cual indica a la rutina de salida tomar un valor decimal y desplegarlo en el monitor. Despus de la d encontramos a la ahora familiar \n para el retorno del cursor y por ltimo el cierre de parntesis. Todos los caracteres entre parntesis definen el patrn de datos a desplegar por el enunciado, luego est una coma seguida por el nombre de la variable indice. Aqu es donde la funcin printf ( ) obtiene el valor decimal como se lo indic %d segn vimos. El sistema sustituye el valor actual de la variable llamada indice por los smbolos %d y los muestra en el monitor.

76312839.doc

Pgina 4 de 59

HVF

COMENTARIOS EN UN PROGRAMA C.
Agregamos comentarios al cdigo C de un programa para hacerlo mas entendible para Usted pero carente de significado para el compilador, por lo que le indicamos al compilador ignorar completamente los comentarios encerrndolos en caracteres especiales. La combinacin de lnea diagonal y asterisco se usa en C para delimitar comentarios como podemos ver en el siguiente cdigo, observe que este programa ilustra una mala tcnica al hacer comentarios pero a su vez muestra donde pueden situarse los comentarios. # include &ltstdio.h> /* Este es un comentario que el compilador ignora */ int main() /* Este es otro comentario ignorado por el compilador*/ { printf("Buscamos como son utilizados los comentarios "); /* Un comentario esta permitido continuar en otra lnea */ printf ("en C.\n"); return 0; } /* Agregamos aqu un comentario ms... */ La combinacin de lnea diagonal y asterisco en la lnea 2 introduce el primer comentario mientras que la combinacin de asterisco y lnea diagonal finaliza el comentario en esa lnea. Observe que este comentario est antes del principio del programa lo que ilustra que un comentario puede preceder al programa en s. Una buena prctica de programacin es incluir un comentario antes del inicio del programa con una breve descripcin del mismo. Observe que el comentario inicia con la combinacin de lnea diagonal y asterisco finalizando con la combinacin de asterisco y lnea diagonal, as en ese orden ( /* Texto del comentario */ ). Es muy importante que no deje espacio alguno entre el asterisco y la lnea diagonal pues de lo contrario el compilador no sabr que se trata de un comentario y por ende se generan mensajes de error. En el siguiente cdigo podemos ver un ejemplo de un programa con un formato de comentarios bien hecho. Con la experiencia que Usted ha ganado hasta el momento es fcil comprender el programa en su totalidad, el compilador ignora todo el espacio extra en tanto que el retorno de cursor le da amplia libertad al momento de darle formato al cdigo del programa. Compile el programa y observe el resultado. #include &ltstdio.h> int main() / * Aqu empieza el programa* / { printf ("Un buen formato"); printf ("puede ayudar a "); printf ("entender un programa.\n"); printf ("Y un mal formato "); printf ("puede convertir un programa"); printf ("en algo difcil de comprender.\n"); return 0; } Ahora observe este otro cdigo. En cuanto tiempo comprendi su funcionamiento ? Para el compilador no importa el formato que Usted utilice, pero a Usted s que le importar cuando trate de resolver un problema relacionado con el cdigo de su programa (Tcnica conocida como debbuging). Compile y ejecute el programa, se sorprender que hace lo mismo que el programa anterior, la nica diferencia est en el formato de la escritura del cdigo. # include &ltstdio.h> int main() /* main inicia aqui * / {printf ("Un buen formato ");printf ("puede ayudar a"); printf ("entender un progama.\n") ;printf ("Y un mal formato ");printf("puede convertir un programa "); printf ("en algo difcil de entender.\n");return 0;} En estos momentos no se preocupe mucho por el formato de sus programas. Tiene mucho tiempo para desarrollar un estilo propio conforme avance en su aprendizaje del lenguaje C. Sea Usted crtico de los estilos que vea en programas C en libros y revistas.

76312839.doc

Pgina 5 de 59

HVF

EL BUCLE while
El lenguaje de programacin C contiene varias instrucciones condicionales y de bucle, en este captulo las trataremos todas ellas empezando con el bucle while. El bucle while se ejecuta mientras una condicin es cierta. Cuando esta condicin se torna falsa, el bucle termina su operacin. Veamos el siguiente ejemplo: /* Este es un ejemplo del bucle while. */ # include <stdio.h int main() { int contador; contador = 0; while (contador < 6) { printf ("El valor de contador es %d\n", contador); contador = contador + 1; } return 0 ; } / * Resultado de la ejecucin del programa El valor de contador es 0 El valor de contador es 1 El valor de contador es 2 El valor de contador es 3 El valor de contador es 4 El valor de contador es 5 */ En este programa empezamos con un comentario y el punto de entrada main ( ), despus definimos una variable de tipo entero a la que llamamos contador dentro del cuerpo del programa, esta variable es inicializada a cero para despus entrar en el bucle while. La sintaxis del bucle while es justamente como se muestra en el programa. A la palabra clave while le sigue una expresin de algo entre parntesis y luego una serie de enunciados encerrados entre llaves. Tan pronto como la expresin entre parntesis es verdadera todos los enunciados entre las llaves se ejecutarn repetidamente. En este caso, debido a que la variable contador es incrementada en 1 cada que los enunciados entre llaves son ejecutados, eventualmente se alcanzar el valor de 6. En este punto los enunciados no se ejecutarn mas porque contador ya no es menor que 6 finalizando as el bucle. El programa continuar entonces con los enunciados que siguen a las llaves. La expresin de comparacin entre parntesis de la instruccin while la trataremos en el siguiente captulo, antes debemos hacer algunas observaciones respecto al bucle. Primero, si la variable contador fuera inicializada a un valor mayor de 5, los enunciados dentro de las llaves podran no ejecutarse por lo que es posible tener un bucle que jams se ejecute. Segundo, si la variable no se incrementa dentro del bucle este jams terminara y por ende el programa. Finalmente, en el caso de existir un solo enunciado por ejecutar entonces no es necesario el uso de llaves. A partir de este programa veremos el resultado de la ejecucin del mismo en forma de comentarios y en algunas veces mostraremos imgenes del programa ejecutado al final del cdigo. Tambin continuaremos ignorando el significado de los enunciados #include y return ya que se explicarn posteriormente.

EL BUCLE do-while
Tenemos ahora una variacin del bucle while en nuestro siguiente ejemplo, este programa es casi idntico al ejemplo anterior excepto que el bucle inicia con la palabra clave do, seguida por una serie de enunciados compuestos entre llaves, despus viene la palabra clave while y finalmente la expresin de evaluacin entre parntesis. /* Este es un ejemplo del bucle do-while */ # include <stdio.h int main() {

76312839.doc int i; i = 0; do { printf ( "El valor de i es ahora %d\n", i ); i = i + 1; } while (i < 5); return 0; }

Pgina 6 de 59

HVF

Los enunciados entre llaves se ejecutan repetidamente en tanto que la expresin entre parntesis sea verdadera. Cuando la expresin es falsa, la ejecucin del bucle termina y el control del programa pasa a los enunciados siguientes. Respecto al bucle dowhile debemos apuntar lo siguiente. En primer lugar, debido a que la prueba verdadero-falso se hace al final del bucle, los enunciados dentro de las llaves se ejecutan al menos una vez. En segundo, si la variable i no cambia dentro del bucle entonces el programa jams terminara. Observe adems que los bucles pueden anidarse, esto es, un bucle puede contener dentro de sus enunciados otro bucle. El nivel de anidamiento no tiene lmite y esto lo ilustraremos mas adelante.

EL BUCLE for
/* Este es un ejemplo del bucle for */ #include <stdio.h int main() { int indice; for(indice = 0 ; indice /* Resultado de la ejecucin: El valor de indice es 0 El valor de indice es 1 El valor de indice es 2 El valor de indice es 3 El valor de indice es 4 El valor de indice es 5 */ El bucle for consiste de la palabra clave for seguida de una expresin entre parntesis. Esta expresin se compone realmente de tres campos cada uno separado por un punto y coma. El primer campo contiene la expresin "indice = 0" y se le llama campo de inicializacin. Cualquier expresin en este campo se ejecuta antes del inicio del bucle, en trminos generales se puede decir que no existe lmite en el contenido del primer campo ya que es posible contener varios enunciados separados por comas sin embargo es buena prctica de programacin mantener las cosas simples. El segundo campo, que en este caso contiene "indice " es la prueba que se hace al principio de cada ciclo del bucle y puede ser cualquier expresin que pueda evaluarse a verdadero falso. La expresin del tercer campo se ejecuta en cada ciclo del bucle pero solo hasta que se hayan ejecutado todas las instrucciones contenidas dentro del cuerpo principal del bucle, en este campo, como en el primero es posible contener varias expresiones separadas por comas. En seguida de la expresin for ( ) estn uno o varios enunciados que conforman el cuerpo ejecutable del bucle. Un enunciado compuesto es cualquier grupo de instrucciones vlidas en C encerradas entre llaves. Un bucle while es til cuando se desconoce cuantas veces ser ejecutado un bucle, en tanto que la instruccin for se usa generalmente en aquellos casos en donde debe existir un nmero fijo de interacciones, adems, el bucle for es conveniente porque contiene toda la informacin del control del bucle en un solo lugar, dentro de un parntesis. Es de su eleccin utilizar uno u otro bucle y dependiendo de cmo sean utilizados cabe la posibilidad con cada uno de estos bucles de no ejecutar las instrucciones dentro del cuerpo del bucle, esto es porque la prueba se hace al principio del bucle y en la primera interaccin puede fallar, sin

76312839.doc

Pgina 7 de 59

HVF

embargo con la instruccin do-while tenemos la seguridad de ejecutar el cuerpo del bucle al menos una sola vez porque la prueba en este bucle se hace al final del ciclo.

EL ENUNCIADO if
Con la instruccin if tenemos el primer ejemplo de un enunciado condicional. Observe en primera instancia la presencia de un bucle for con un enunciado compuesto que contiene dos instrucciones if. Este es a su vez un ejemplo de instrucciones anidadas, est claro que cada una de las instrucciones if ser ejecutada 8 veces. /* Ejemplo de los enunciados if e if-else */ # include <stdio.h int main() { int valor; for(valor = 0 ; valor < 5) printf("Este mensaje se muestra cuando valor que es %d es menor que 5\n", valor); else printf("Este mensaje se muestra cuando valor que es %d es mayor que 4\n", valor); } /* Fin del bucle */ printf("Este mensaje se mostrara solo cuando finalice el bucle \n"); return 0; } Veamos el primer enunciado if, este empieza con la palabra clave if seguida de una expresin entre parntesis, si esta es evaluada a verdadero se ejecuta la instruccin que le sigue, pero si es falso se brinca esta instruccin y contina la ejecucin en los siguientes enunciados. La expresin "valor == 2" est simplemente preguntando si el valor de valor es igual a 2, observe que se est utilizando un doble signo de igual para evaluar a verdadero cuando valor vale 2, utilizar "valor == 2" tiene un significado completamente diferente que explicaremos mas adelante. La segunda instruccin if es similar a la primera excepto por la adicin de la palabra clave else en la lnea 14, esto significa que si el enunciado entre parntesis se evala a verdadero se ejecuta la primera expresin, de lo contrario la instruccin que sigue a else ser ejecutada, por lo que una de las dos instrucciones ser siempre ejecutada. Observe adems que el programa imprime dos mensajes diferentes cuando valor vale 2, esto es as porque tenemos dos condiciones verdaderas cuando valor es 2, esto es, cuando valor es exactamente 2 y a la vez es menor que 5. Compile este programa y observe su funcionamiento.

LOS ENUNCIADOS break Y continue


Para ver un ejemplo de estas dos instrucciones, estudiemos el siguiente cdigo: # include < stdio.h int main() { int xx; for(xx = 5 ; xx /* Resultado de la ejecucin: Este bucle se ejecuta cuando xx es menor de 8, ahora xx es 5 Este bucle se ejecuta cuando xx es menor de 8, ahora xx es 6 Este bucle se ejecuta cuando xx es menor de 8, ahora xx es 7 Ahora xx es diferente de 8, xx tiene el valor de 5 Ahora xx es diferente de 8, xx tiene el valor de 6 Ahora xx es diferente de 8, xx tiene el valor de 7 Ahora xx es diferente de 8, xx tiene el valor de 9 Ahora xx es diferente de 8, xx tiene el valor de 10 Ahora xx es diferente de 8, xx tiene el valor de 11

76312839.doc

Pgina 8 de 59

HVF

Ahora xx es diferente de 8, xx tiene el valor de 12 Ahora xx es diferente de 8, xx tiene el valor de 13 Ahora xx es diferente de 8, xx tiene el valor de 14 */ Observe que en el primer bucle for, existe una instruccin if que llama a un break si xx es igual a 8. La instruccin break lleva al programa a salirse del bucle que se estaba ejecutando para continuar con los enunciados inmediatos al bucle, terminando ste en forma efectiva. Se trata de una instruccin muy til cuando se desea salir del bucle dependiendo de los resultados que se obtengan dentro del bucle. En este caso, cuando xx alcanza el valor de 8, el bucle termina imprimiendo el ltimo valor vlido, 7. La instruccin break brinca inmediatamente despus de la llave que cierra el bucle. En el siguiente bucle for que empieza en la lnea 12, contiene un enunciado continue el cual no finaliza el bucle pero suspende el presente ciclo. Cuando el valor de xx alcanza como en este caso, el valor de 8, el programa brincar al final del bucle para continuar la ejecucin del mismo, eliminando as la instruccin printf ( ) cuando en el bucle xx alcanza el valor de 8. El enunciado continue siempre brinca al final del bucle, justo antes de la llave que indica el fin del bucle.

LA INSTRUCCIN switch
Estudiaremos ahora una de las instrucciones mas importantes del lenguaje C, el enunciado switch, sta no es difcil as que no permita que lo intimide. Empieza con la palabra clave switch seguida por una variable entre parntesis la cual es la variable de conmutacin, en este ejemplo truck. Las condiciones de conmutacin se encierran entre llaves. La palabra reservada case se utiliza para empezar cada condicin, le sigue el valor de la variable para la condicin seleccionada, despus un smbolo de colon (dos puntos) y por ltimo los enunciados a ser ejecutados. # include <stdio.h int main() { int pato; for (pato = 3 ; pato La mejor manera de entender el funcionamiento de la instruccin switch es compilando y ejecutando el programa de este ejemplo, cuando la variable pato vale 3 la instruccin switch causa que el programa brinque directamente a la lnea 9 donde printf ( ) despliega "pato vale tres" y el enunciado break hace brincar la ejecucin del programa fuera del bucle de instrucciones de switch. Cuando el valor de la variable pato est especificado en una instruccin case dentro del bucle de instrucciones de switch, los enunciados del programa sern ejecutados en orden hasta encontrar una instruccin break cuyo funcionamiento se explic en el prrafo anterior. En el ejemplo que presentamos, cuando pato vale 5, este valor est asociado a una instruccin case pero como no est asociada ninguna instruccin para ejecutar, el programa contina hasta encontrar una instruccin ejecutable, que en el ejemplo es printf ( ) de la lnea 15. En el caso en que el valor de pato no est asociado con una instruccin case se ejecuta el enunciado especificado en la instruccin default. La instruccin switch no se usa con la misma frecuencia que el bucle o el enunciado if, de hecho se usa muy poco, sin embargo debe ser comprendida completamente por el programador C serio.

EL ENUNCIADO goto
Para utilizar este enunciado simplemente use la palabra clave goto seguida por el nombre simblico a donde se quiera hacer el salto, este nombre se coloca en cualquier parte del cdigo seguido de un smbolo de colon (dos puntos). Usted puede brincar a donde quiera, pero no est permitido hacerlo hacia el interior de un bucle, aunque si esta permitido brincar fuera del bucle. # include <stdio.h int main () { int uno, dos, tres; casa_del_lobo: { printf("Auuuuuuuuuuu \n"); if(uno==1) if(dos==2) if(tres==3) {

76312839.doc

Pgina 9 de 59

HVF

printf("Este es el ultimo mensaje... \n"); printf("La suma de uno, dos y tres es %d \n", (uno+dos+tres)); goto fin_del_programa; } printf("Todavia no termina el programa... \n"); } goto inicio; otro_lugar: { printf("Estamos perdidos... \n"); tres=3; printf("tres vale %d \n", tres); goto un_lugar_mas; } un_lugar_desconocido: { dos=2; printf("Este no es el inicio del programa... \n"); printf("Con goto se puede brincar fuera de un bucle... \n"); goto casa_del_lobo; } goto fin_del_programa; inicio: { uno=1; printf("Ahora uno vale %d \n", uno); } goto otro_lugar; un_lugar_mas: printf("Este lugar es solo para brincar a otro lado... \n"); goto un_lugar_desconocido; fin_del_programa: return 0; } Este programa en particular es un verdadero embrollo pero es un buen ejemplo de porque los desarrolladores de software estn tratando de eliminar el uso del enunciado goto tanto como sea posible. Algunas personas opinan que goto no debera utilizarse nunca, esto es un criterio reducido pues si Usted se llegara a encontrar en una situacin donde el uso de goto facilita la ejecucin del programa sienta plena libertad de utilizar la sentencia goto. A los cdigos escritos sin sentencias goto se les suele conocer con el nombre de "Programacin Estructurada". Un buen ejercicio podra consistir en re-escribir el cdigo para ver en que manera se vuelven los enunciados mas legibles cuando estn enlistados en orden. A lo largo de este captulo nos referiremos al rango de una variable, esto significa los lmites de valores que pueden ser almacenadas en una variable dada. Su compilador puede usar rangos diferentes para algunas variables debido a que el estndar ANSI no define lmites especficos para todos los tipos de datos. Consulte la documentacin de su compilador para saber el rango exacto para cada tipo de variable.

ASIGNANDO ENTEROS.
En el primer programa de este captulo veremos ejemplos de enunciados de asignacin. Tres variables estn definidas para usarse en este programa y el resto del cdigo consiste en una serie de ilustraciones de varios tipos de asignacin. Las tres variables estn definidas en una sola lnea e inicialmente almacenan valores desconocidos. Las primeras dos lneas de asignacin, lneas 6 y 7, asignan valores numricos a las variables llamadas a y b, las cinco lneas siguientes ilustran las cinco funciones aritmticas bsicas y cmo usarlas. La quinta funcin se llama operador modulo y devuelve el resto cuando las dos variables son divididas, solo se puede aplicar a variables de tipo entero mismas que definiremos mas adelante. Las siguientes dos lneas demuestran cmo combinar algunas variables en expresiones matemticas relativamente complejas. Todos estos ejemplos tienen un uso meramente ilustrativo.

76312839.doc

Pgina 10 de 59

HVF

/* Este programa ilustra diversos enunciados de asignacin */ int main() { int a, b, c; /* Variables de tipo entero para los ejemplos */ a = 12; b = 3; c = a + b; /* suma simple */ c = a - b; /* substraccin resta */ c = a * b; /* multiplicacin */ c = a / b; /* divisin */ c = a % b; /* modulo (resto) */ c = 12*a + b/2 - a*b*2/(a*c + b*2); c = c/4+13*(a + b)/3 - a*b + 2*a*a; a = a + 1; /* incremento de variable */ b = b * 5; a = b = c = 20; /* asignacin mltiple */ a = b = c = a + b * c/ 3; a = (b = (c = 20)); /* Igual a la lnea 18 */ return 0 ; } /* Resultado de la ejecucin: (Este programa no tiene salida.) */ La procedencia de los operadores es un tpico muy importante que Usted necesita estudiar en detalle en algn momento, por lo pronto necesitamos unas cuantas reglas. Cuando se tienen expresiones aritmticas combinadas, los operadores de multiplicacin y divisin se completan antes que los operadores de suma y resta estando en el mismo nivel lgico, as cuando evaluamos a*b+c/d, la multiplicacin y la divisin se ejecutan primero y despus la suma. Sin embargo, en la expresin a*(b+c/d), la suma sigue de la divisin y posteriormente la multiplicacin porque las operaciones estn en niveles lgicos diferentes como lo define el uso del parntesis. Los enunciados en las lneas 15 y 16 son perfectamente aceptables como estn, pero como veremos mas adelante en este captulo, hay otra forma de escribir estas sentencias con un cdigo mas compacto. En las lneas 17 y 18 se dan ejemplos de asignacin mltiple. El compilador C rastrea el enunciado de asignacin de derecha a izquierda, resultando un constructor muy til. El compilador encuentra el valor 20, lo asigna a c, entonces contina a la izquierda encontrando que el resultado del ltimo clculo debe asignarse a b. Como este resultado fue 20 asigna a su vez ste valor a b y contina rastreando a la izquierda asignando el valor de 20 a la variable a. Este es un constructor muy til cuando Usted inicializa un grupo de variables. La lnea 18 ilustra que es posible efectuar algunos clculos antes de asignar el resultado a un grupo de variables. Los valores de a, b y c, antes del principio de la lnea 18 son utilizados para el clculo, el resultado se asigna posteriormente a cada una de las tres variables.

PRIMERO DEFINIR, DESPUS EJECUTAR.


Aqu es un buen momento para definir una regla a seguir en C. La definicin de variables se d siempre antes que cualquier enunciado ejecutable en un bloque de programa, si Usted trata de definir una variable despus de algunos enunciados ejecutables su compilador marcar un error. Un bloque de programa es uno o mas enunciados encerrados por llaves. El bloque puede incluso estar vaco como se d en el caso de las etapas tempranas del desarrollo de un programa.

TIPOS DE DATOS ADICIONALES.


En estos momentos debe estar ya familiarizado con el tipo de dato entero (int), en el siguiente ejemplo le presentamos dos tipos nuevos, char y float. El tipo de dato char es casi igual al entero excepto que solo se le pueden asignar valores entre -128 y 127 en la mayora de las implementaciones de C para microcomputadoras debido a que generalmente es almacenado en un byte de memoria. Algunas implementaciones de C utilizan un elemento de memoria mayor para char dandole as un mayor rango de valores tilies. El tipo char se usa generalmente para datos ASCII, comunmente conocido como texto. El texto que Usted est leyendo fue escrito en una computadora con un procesador de texto que almacena las palabras en la computadora un carcter por byte. En contraste, el tipo de dato int se almacena en las modernas computadoras de 32 bits en cuatro bytes por dato de tipo int. Tenga en mente que aunque el

76312839.doc

Pgina 11 de 59

HVF

tipo de dato char fue diseado para almacenar representaciones de caracteres ASCII, puede ser utilizado a su vez para almacenar datos de valor pequeo, veremos mas de este tema cuando estudiemos cadenas en un captulo posterior. /* Tipos de datos nuevos */ int main() { int a, b, c; /* Entero, de -32768 a 32767 sin punto decimal */ char x, y, z; /* De -128 a 127 sin punto decimal */ float numero, gato, casa; /* De 3.4E-38 a 3.4E+38 con punto decimal */ a = b = c = -27; x = y = z = 'A'; numero = gato = casa = 3.6792; a = y; /* a es ahora 65 (carcter A) */ x = b; /* x es ahora -27 */ numero = b; /* num ser -27.00 */ a = gato; /* a tomar el valor de 3 */ return 0; } /* Resultado de la ejecucin: (Este programa no tiene salida.) */

MEZCLANDO TIPOS DE DATOS.


Es conveniente discutir la manera en que C maneja los tipos de datos int y char. La mayora de las operaciones en C que estn diseadas para trabajar con variables de tipo entero trabajarn igualmente bien con variables de tipo carcter porque estas son variables enteras, es decir, no tienen parte fraccionaria, por esta razn es posible combinar tipos de datos int y char en casi cualquier forma que Usted desee, el compilador no se confundir pero es posible que Usted s por lo que es recomendable utilizar el tipo de dato adecuado para la variable en cuestin. El otro tipo de dato nuevo es float, comunmente llamado dato de punto flotante el cual generalmente tiene un rango muy grande, un relativo nmero grande de dgitos significativos y un nmero mayor de palabras lgicas son requeridas para almacenarlo. El tipo de dato float tiene un punto decimal asociado por lo que se requieren varios bytes de memoria para almacenar una sola variable de tipo float. Las primeras tres lneas del programa asignan valores a las nueve variables definidas por lo que podemos manipular algunos de los datos entre los diferentes tipos de variables. Como ya mencionamos, el tipo de dato char es en realidad un tipo de dato entero el cual es promovido a tipo int cuando es necesario sin requerir especiales consideraciones, de la misma manera un campo de datos de tipo char puede ser asignado a una variable int, lo contrario es tambin posible siempre y cuando el valor de la variable est dentro del rango del tipo char, posiblemente de -128 a 127. Si el valor cae fuera de este rango, la mayora de los compiladores C simplemente truncan los bits mas significativos y usan los bits menos significativos. La lnea 13 ilustra la facilidad de convertir un tipo int en float, sencillamente se asigna el nuevo valor y el sistema hace la conversin adecuada, sin embargo, al convertir de float en int existe una complicacin dada la posibilidad de la presencia de una parte fraccionaria en un nmero de punto flotante, el sistema debe decidir que hacer con esta parte, por definicin se truncar la parte fraccionaria. Algunas constantes tiles estn disponibles para su uso al determinar lmites de rango en los tipos estndard, por ejemplo, los nombres INT_MIN e INT_MAX estn disponibles en el archivo "limits.h" como constantes los cuales pueden ser utilizados en su cdigo. INT_MAX es el nmero mas grande posible que su compilador puede utilizar para una variable de tipo int. El archivo "limits.h" contiene un gran nmero de lmites que Usted puede utilizar simplemente incluyendo este archivo en su programa. Se recomienda ampliamente estudiar el archivo limits.h.

MAS TIPOS DE VARIABLES.


El siguiente ejemplo contiene la mayora de los tipos de datos estndard disponibles en C, consulte la documentacin de su compilador para una lista completa de los tipos disponibles con su compilador, existen adems otros tipos, los llamados compuestos (p.e. arrays y estructuras) que sern cubiertos a su debido tiempo en este tutorial. # include <stdio.h>

76312839.doc int main() { int a; /* entero simple */ long int b; /* entero largo */ short int c; /* entero corto */ unsigned int d; /* entero unsigned */ char e; /* carcter */ float f; /* punto flotante */ double g; /* punto flotante de doble precisin */ a = 1023; b = 2222; c = 123; d = 1234; e = 'X'; f = 3.14159; g = 3.1415926535898; printf("a = %d\n", a); /* salida decimal */ printf("a = %o\n", a); /* salida octal */ printf("a = %x\n", a); /* salida hexadecimal */ printf("b = %ld\n", b); /* salida decimal largo */ printf("c = %d\n", c); /* salida decimal corto */ printf("d = %u\n", d); /* salida unsigned */ printf("e = %c\n", e); /* salida carcter */ printf("f = %f\n", f); /* salida flotante */ printf("g = %f\n", g); /* salida flotante doble */

Pgina 12 de 59

HVF

printf("\n"); printf("a = %d\n", a); /* salida entero simple */ printf("a = %7d\n", a); /* usa una amplitud de 7 campos */ printf("a = %-7d\n", a); /* justificado por la izquierda con 7 campos */ c = 5; d = 8; printf("a = %*d\n", c, a); /* utiliza 5 campos */ printf("a = %*d\n", d, a); /* utiliza 8 campos */ printf("\n"); printf("f = %f\n", f); /* salida flotante */ printf("f = %12f\n", f); /* 12 campos */ printf("f = %12.3f\n", f); /* 12 campos y 3 decimales */ printf("f = %12.5f\n", f); /* 5 decimales */ printf("f = %-12.5f\n", f); /* justificado a la izquierda */ return 0; } Con la introduccin del estndard ANSI-C dos palabras clave han sido agregadas a C, estas no estn ilustradas en los ejemplos pero las discutiremos aqu, estas son const y volatile y se utilizan para decirle al compilador que las variables de estos tipos necesitarn especial consideracin. Cuando una variable es declarada como const su valor no padr ser cambiado por el programa, si Usted trata inadvertidamente de modificar una entidad const el compilador generar un mensaje de error. Cuando utilizamos volatile declaramos que el valor puede ser cambiado por el programa y adems puede ser cambiado por una entidad externa como puede ser un pulso de actualizacin de reloj almacenado en una variable. Ejemplos: const int index1 = 5; /* Una variable const debe inicializarse siempre */ const index2 = 6; const float valor_grandote = 1245.12; volatile const int index3 = 45; volatile int index4;

76312839.doc

Pgina 13 de 59

HVF

CARACTERES DE CONVERSION.
Enseguida tenemos una lista de algunos de los caracteres de conversin y la forma en que son utilizados con la instruccin printf ( ), una lista completa de los caracteres de conversin debe estar includa en la documentacin de su compilador, no se preocupe si por el momento no los entiende, es suficiente saber que cuenta con una gran flexibilidad disponible para cuando Usted est listo para utilizarlas. d i o x u c s f Notacin decimal Notacin decimal (Nueva extensin ANSI) Notacin octal Notacin hexadecimal Notacin unsigned Notacin carcter Notacin de cadena Notacin de punto flotante

Cada uno de estos caracteres de conversin se utilizan despus del signo de porcentaje (%) para indicar el tipo de salida deseada, los siguientes campos pueden agregarse entre estos dos caracteres: Justificacin por la izquierda en su campo (n) Amplitud de campo Separa (n) de (m) (m) Dgitos significativos en punto flotante l Largo Todos estos caracteres de conversin se utilizaron en el ejemplo anterior excepto la notacin de cadena, misma que ser cubierta mas adelante.

COMPARACIONES LGICAS.
En el siguiente cdigo mostramos una gran variedad de enunciados de comparacin, empezamos definiendo e inicializando nueve variables para ser utilizados en las comparaciones. El primer grupo es el mas simple porque la comparacin se d solo entre dos variables, cualquier variable puede ser reemplazada por una constante y aun seguir siendo vlida la comparacin, pero utilizar dos variables es el caso mas general. Observe que en este ejemplo hemos introducido el operador de negacin que se representa con el smbolo de admiracin ! , observe adems que los comparadores lgicos "menor que" y "mayor igual que" estn tambin disponibles pero no se ilustran en el ejemplo. Para comprender algunos de los enunciados del ejemplo debemos entender lo que significa verdadero o falso en lenguaje C. Falso est definido como cero, y verdadero es cualquier valor diferente de cero, en los compiladores ANSI-C este valor es 1, sin embargo es recomendable como buena prctica de programacin no utilizar este valor para ningn clculo sino solo para propsitos de control. Cualquier variable de tipo int o char puede utilizarse para una evaluacin de verdadero-falso. En el tercer grupo del ejemplo se introducen los conceptos de los operadores lgicos "and" ( && ) en el cual el resultado de la comparacin es verdadero si ambas partes del enunciado && son verdaderas, y "or" ( || ) en donde la expresin se evala como verdadera si alguna de las dos partes de || es verdadera. Veamos el ejemplo. int main() /* Comparaciones lgicas */ { int x = 11, y = 11, z = 11; char a = 40, b = 40, c = 40; float r = 12.987, s = 12.987, t = 12.987; /* Primer grupo de enunciados de comparacin */ if (x == y) z = -13; /* z = -13 */ if (x > z) a = 'A'; /* a = 65 */ if (!(x > z)) a = 'B'; /* No habr cambios */ if (b <= c) r = 0.0; /* r = 0.0 */ if (r != s) t = c/2; /* t = 20 */

76312839.doc

Pgina 14 de 59

HVF

/* Segundo grupo de enunciados de comparacin */ if (x = (r != s)) z = 1000; /* x = algn nmero positivo, z = 1000 */ if (x = y) z = 222; /* x = y, y z = 222 */ if (x != 0) z = 333; /* z = 333 */ if (x) z = 444; /* z = 444 */ /* Tercer grupo de comparacin */ x = y = z = 77; if ((x == y) && (x == 77)) z = 33; /* z = 33 */ if ((x > y) || (z > 12)) z = 22; /* z = 22 */ if (x && y && z) z = 11; /* z = 11 */ if ((x = 1) && (y = 2) && (z = 3)) r = 12.00; /* x = 1, y = 2, z = 3, r = 12.00 */ if ((x == 2) && (y = 3) && (z = 4)) r = 14.56; /* Ningn cambio */ /* Cuarto grupo de comparacin */ if (x == x); z = 27.345; /* z siempre cambia */ if (x != x) z = 27.345; /* Nada cambia */ if (x = 0) z = 27.345; /* x = 0, z no cambia */ return 0; } /* Resultado de la ejecucin: (Este programa no tiene salida.) */

CONSTRUCCIONES TILES EN C.
Existen tres constructores en C que a primera vista no tienen sentido porque no son intuitivos, pero pueden incrementar la eficiencia del cdigo compilado y son utilizados extensivamente por los programadores de C experimentados, Usted debe aprender a utilizarlos debido a que aparecen en prcticamente todos los programas que Usted ver en publicaciones, veamos estos nuevos constructores: int main() { int x = 0, y = 2, z = 1025; float a = 0.0, b = 3.14159, c = -37.234; /* incremento */ x = x + 1; /* incremento de x */ x++; /* post-incremento de x */ ++x; /* pre-incremento de x */ z = y++; /* z = 2, y = 3 */ z = ++y; /* z = 4, y = 4 */ /* decremento */ y = y - 1; /* decremento de y */ y--; /* post-decremento de y */ --y; /* pre-decremento de y */ y = 3; z = y--; /* z = 3, y = 2 */ z = --y; /* z = 1, y = 1 */

76312839.doc

Pgina 15 de 59

HVF

/* operaciones aritmticas */ a = a + 12; /* Se suma 12 a la variable a */ a += 12; /* Se suman otros 12 a la variable a */ a *= 3.2; /* Multiplica a por 3.2 */ a -= b; /* Resta b de a */ a /= 10.0; /* Divide a entre 10.0 */ /* enunciados condicionales */ a = (b >= 3.0 ? 2.0 : 10.5 ); /* Esta expresin */ if (b >= 3.0) /* Y esta expresin */ a = 2.0; /* son idnticas, ambas */ else /* causarn el mismo */ a = 10.5; /* resultado */ c = (a > b ? a : b); /* c tendr el mayor valor de a b */ c = (a > b ? b : a); /* c tendr el valor menor de a b */ return 0; } /* Resultado de la ejecucin: (Este programa no tiene salida.) */ En la lnea 8 simplemente se agrega 1 al valor de x, los siguientes dos enunciados tambin agregan uno al valor de x, pero no es tan intuitivo respecto a su funcionamiento. Por definicin del lenguaje C un doble signo de mas (++) ya sea antes despus de la variable, incrementa sta en uno, adicionalmente si los signos mas estn despus de la variable, sta se incrementa despus de utilizarla, por el contrario, si los signos mas estn antes de la variable, sta se incrementa y despus se utiliza. En el siguiente grupo se analiza el decremento de la variable aplicandose las mismas reglas que para el incremento de la variable. Los operadores aritmticos por su parte se utilizan para modificar cualquier variable por algn valor constante, en la lnea 25 se suma 12 a la variable a, en tanto que en la lnea 26 el resultado es el mismo, solo que no es tan intuitiva como la instruccin anterior. Colocando el operador deseado antes del signo igual y eliminando la segunda referencia a la variable, esto se puede hacer con los cuatro operadores aritmticos. Al igual que los operadores de incremento y decremento, los operadores aritmticos son utilizados con frecuencia por los programadores experimentados por lo que es muy recomendable su familiarizacin con el uso de estos operadores. El operador condicional consiste de tres expresiones separadas por un signo de interrogacin y por un signo colon (dos puntos). El enunciado previo al signo de interrogacin es evaluada a falso-verdadero, si es verdadero, el enunciado que est entre el signo de interrogacin y el signo colon se valora, por el contrario, la expresin posterior al signo colon es valorada. El resultado es idntico si se utiliza una expresin if con una clausula else pero la expresin condicional tiene la ventaja de ser mas compacta y por lo tanto compilar pocas instrucciones en el programa final. Este ha sido un captulo largo, sin embargo contiene informacin importante para ser un buen programador C, en el siguiente captulo analizaremos la construccin de bloques de C, las funciones, en ese punto Usted tendr a su alcance los materiales bsicos que el permitirn escribir programas tiles y aplicables a la vida real.

CMO DEFINIR UNA FUNCIN


Empezemos este captulo estudiando este cdigo: # include <stdio.h> int suma; /* Esta es una variable global */ int main() { int indice;

76312839.doc

Pgina 16 de 59

HVF

encabezado(); /* Aqu se llama a la funcin llamada header */ for (indice = 1 ; indice <= 7 ; indice ++) cuadrado (indice); /* Llama a la funcin cuadrado */ final(); /* Llama a la funcin final */ return 0; } encabezado () /* Esta es la funcin llamada encabezado */ { suma = 0; /* Inicializa la variable "suma" */ printf("Este es el encabezado para el programa cuadratico \n\n"); } cuadrado (numero) /* Esta es la funcin cuadrado */ int numero; { int numero_cuadrado; numero_cuadrado = numero * numero; /* Esta genera el valor cuadrtico */ suma += numero_cuadrado; printf("El cuadrado de %d es %d\n", numero, numero_cuadrado); } final () /* Esta es la funcin final */ { printf("\nLa suma de los cuadrados es %d\n", suma); } Note la parte ejecutable de este programa que empieza en la lnea 9 con un enunciado que dice simplemente "encabezado ( ) ;", la cual es la manera de llamar a una funcin. El parntesis es necesario porque el compilador C lo utiliza para determinar que se trata de una llamada a funcin y no simplemente una variable mal colocada. Cuando el programa llega a esta lnea de cdigo la funcin llamada encabezado ( ) es llamada, sus enunciados son ejecutados y el control regresa a los enunciados que le siguen a la llamada. Continuando nos encontramos con un bucle for que ser ejecutado siete veces en donde est otra llamada a una funcin denominada cuadrado( ). Finalmente encontramos otra funcin llamada final ( ) que ser llamada y ejecutada. Por el momento ignoraremos la variable indice en el parntesis de la llamada a cuadrado ( ). En seguida del programa principal podemos ver el principio de una funcin en la lnea 18 que cumple con las reglas establecidas para el programa principal excepto que su nombre es encabezado ( ). Esta es la funcin que llamamos desde la lnea 9 del programa principal. Cada uno de sus enunciados sern ejecutados y una vez completos el control retorna al programa principal, o mas propiamente dicho, a la funcin main ( ). El primer enunciado le asigna a la variable llamada suma el valor de cero ya que planeamos utilizarla para acumular la suma de los cuadrados. Como la variable llamada suma fue definida antes del programa principal est disponible para utilizarla en cualquiera de las funciones que se han definido posteriormente. A una variable definida de esta manera se el llama global y su alcance es el programa completo incluyendo todas las funciones. En la lnea 21 se despliega un mensaje en el monitor y despus el control retorna a la funcin main ( ). En la llamada a la funcin cuadrado ( ), hemos agregado una nueva caracterstica, el nombre de la variable indice dentro del parntesis. Esta es una indicacin al compilador para que cuando brinque a la funcin Usted desea tomar el valor de la variable indice para utlizarlo durante la ejecucin de la funcin. Observando la funcin cuadrado ( ) en la lnea 25 encontramos otro nombre de variable encerrado entre parntesis, la variable numero. Este es el nombre que preferimos para llamar a la variable pasada a la funcin cuando ejecutemos el cdigo dentro de la funcin. Debido a que la funcin necesita saber el tipo de variable, esta se define inmediatamente despus del nombre de la funcin y antes de la llave de apertura de la funcin. En la lnea 26, la expresin "int numero;" le indica a la funcin que el valor que le ha sido pasado ser una variable de tipo int. De esta manera el valor de la variable indice del programa principal pasado a la funcin cuadrado ( ) pero renombrada numero y disponible para utilizarse dentro de la funcin. Este es el estilo clsico para definir variables dentro de una funcin y ha estado en uso desde que fue definido por primera vez el lenguaje C. Un nuevo y mejor mtodo est ganando popularidad debido a sus beneficios y lo discutiremos mas adelante en este captulo. En seguida de la llave de apertura de la funcin definimos otra variable llamada numero_cuadrado para utilizarla dentro de la funcin en s. Establecemos la variable llamada numero_cuadrado como el cuadrado del valor almacenado en numero, despus agregamos numero_cuadrado al total almacenado en suma. De la pasada leccin recordar que "suma += numero_cuadrado;" tiene el mismo significado de "suma = suma + numero_cuadrado;", imprimimos el nmero y su cuadrado en la lnea 32 y retornamos al programa principal.

76312839.doc

Pgina 17 de 59

HVF

Cuando pasamos el valor de la variable indice a la funcin debemos puntualizar lo siguiente: Nosotros no pasamos a la funcin la variable indice, lo que pasamos es una copia del valor, de esta manera el valor original se protege de cambios accidentales dentro de la funcin. Podemos modificar la variable numero como lo requiera la funcin cuadrado( ) y al retornar a la funcin principal la variable indice no ha sido modificada, de esta manera no podemos retornar un valor a la funcin que llama ( main ( ) ) de la funcin llamada (square ( ) ) utilizando este mtodo. Encontraremos un mtodo bien definido para retornar valores a main ( ) o a cualquier funcin que hace la llamada cuando estudiemos arrays y punteros. Hasta entonces la unica manera que tenemos para comunicarnos con la funcin que llama son las variables globales. Continuando en la funcin main ( ) llegamos a la ltima llamada a una funcin denominada final ( ) en la lnea 12. En esta lnea llamamos a la ltima funcin que no tiene variables locales definidas, esta funcin despliega un mensaje con el valor almacenado en suma para finalizar el programa. El programa termina al retornar a la funcin main ( ) y como ya no hay nada que hacer, el programa termina.

CONFESAMOS UNA PEQUEA MENTIRA


Hemos dicho que la nica manera por lo pronto de obtener un valor de una funcin llamada era a traves del uso de variables globales, sin embargo hay otra forma que discutiremos despus de que estudie el siguiente cdigo. En este ejemplo veremos que es fcil regresar un solo valor de una funcin previamente llamada, pero insistimos, para obtener mas de un valor ser necesario recurrir ya sea a un puntero o bien a un array. # include <stdio.h> int main() /* Este es el programa principal */ { int x, y; for( x = 0 ; x < 8 ; x++ ) { y = cuadrado(x); /* Ir para obtener el valor de x*x */ printf ( "El cuadrado de %d es %d\n", x, y ) ; } for( x = 0 ; x < 8 ; ++x ) printf("El cuadrado de %d es %d\n", x, cuadrado(x)); return 0; } cuadrado(entrada) /* Funcin para obtener el cuadrado de "entrada" */ int entrada; { int cuadratica; cuadratica = entrada * entrada; return (cuadratica) ; /* Se asigna cuadrado() = cuadratica*/ } En la funcin main ( ) definimos dos enteros y empezamos un bucle en la lnea 7 el cual ser ejecutado 8 veces, el primer enunciado dentro del bucle es "y = cuadrado (x) ;" que representa una nueva y extraa construccin, de lo que hemos aprendido no tendremos problema para entender que la parte cuadrado(x) del enunciado es una llamada a una funcin denominada cuadrado ( ) tomando el valor de x como parmetro. En al lnea 19 encontramos que la funcin prefiere llamar a la variable de entrada entrada, procede a elevar al cuadrado el valor de entrada y llamar al resultado cuadratica, despus en la lnea 25 tenemos un nuevo enunciado, la instruccin return. El valor dentro del parntesis se asigna a la funcin en s y se retorna como un valor utilizable en el programa principal asignandose este valor a y. El parntesis que encierra el valor retornado en la lnea 25 no es necesario pero la mayora de los programadores C experimentados lo utilizan. Es necesario hacer esta aclaracin, el tipo de variable retornada debe declararse para darle sentido a los datos, si la variable no es declarada, el compilador la asignar como tipo int, si se desea otro tipo especfico debe declararse.

FUNCIONES DE PUNTO FLOTANTE


Veremos ahora un ejemplo de funcin de estilo clsico con retorno de punto flotante. Empieza definiendo una variable global de punto flotante llamada z que ser utilizada posteriormente. Despus, en la parte principal del programa se define un entero seguido

76312839.doc

Pgina 18 de 59

HVF

de dos variables de punto flotante siguiendoles dos definiciones de extrao aspecto. Las expresiones cuadrado( ) y glcuadrado( ) en la lnea 8 parecen llamadas a funcin. Esta es la forma adecuada para definir que una funcin regresar un valor que no es de tipo int sino de otro tipo, en este caso de tipo flotante. Observe que ninguna funcin es llamada en esta lnea de cdigo, simplemente se declara el tipo de dato que retornarn estas dos funciones. Refiriendonos a la funcin cuadrado( ) que empieza en la lnea 28 ver que el nombre es precedido por la palabra clave float, esto lo indica al compilador que esta funcin retornar un valor de tipo float a cualquier programa que las llame. El tipo de dato que retorna la funcin es ahora compatible con la llamada a esta funcin. La siguiente lnea de cdigo contiene "float valor_interno;" lo que le indica al compilador que la variable pasada a esta funcin desde el programa que la llama ser de tipo flotante. La funcin glcuadrado ( ) empieza en la lnea 38 retornar una variable de tipo float pero adems utiliza una variable global para la entrada. El clculo cuadrtico lo hace en el enunciado return y por lo tanto no requiere definir una variable separada para almacenar el producto. La funcin cuadrado ( ) pudo ejecutar el clculo cuadrtico en la instruccin return pero se hizo en forma separada a manera de ilustracin. # include <stdio.h> float z; /* Variable global */ int main() { int indice; float x, y, cuadrado(), glcuadrado(); for(indice = 0 ; indice <= 7 ; indice ++) { x = indice; /* convierte int en float */ y = cuadrado(x); /* el cuadrado de x a una variable de punto flotante */ printf("El cuadrado de %d es %10.4f\n", indice, y); } for (indice = 0 ; indice <= 7 ; indice ++) { z = indice; y = glcuadrado (); printf("El cuadrado de %d es %10.4f\n", indice, y); } return 0; } float cuadrado (valor_interno) /* Eleva al cuadrado un tipo float, retorna un tipo float */ float valor_interno; { float cuadratica; cuadratica = valor_interno * valor_interno; return(cuadratica); } float glcuadrado () /* Eleva al cuadrado un tipo float, retorna un tipo float */ { return(z * z); } En los tres programas que hemos estudiado en este captulo se ha utilizado el estilo clsico para definir funciones, si bien, este fue el primer estilo definido en C existe un mtodo mas reciente que le permite detectar errores con mayor facilidad. Cuando Usted lea artculos de C se encontrar programas que utilizan el estilo clsico por lo que Usted debe estar preparado para interpretarlos correctamente, esta es la razn por lo que incluimos el estilo clsico en este tutorial sin embargo, se recomienda ampliamente que Usted adopte y use el mtodo moderno tal y como est definido por el estndar ANSI-C, mismo que empezaremos a tratar desde este momento y hasta el final de este tutorial.

76312839.doc

Pgina 19 de 59

HVF

EL ENUNCIADO return EN LA FUNCIN main( )


En la definicin original de C, todas las funciones regresaban por default una variable de tipo int a menos que el autor especificara algo diferente, como era explcitamente opcional el retorno de un valor al dejar una funcin, la mayora de los programas C eran escritos de la siguiente manera: main () { } Cuando el prototipado de funciones fue agregado al lenguaje ( el prototipado lo estudiaremos mas adelante ), muchos programadores suponan que la funcin main ( ) no retornaba nada, de esta manera utilizaban el tipo void para el retorno haciendose comn la prctica de escribir la funcin principal como sigue: void main () { } Cuando el estndar ANSI-C estuvo listo el nico tipo de retorno aprovado es una variable int, esto conduce a la siguiente forma de escribir la funcin main ( ): int main () { return 0; } Para asegurar que el cdigo que Usted escriba sea lo mas portable posible utilice la forma arriba descrita. Aparentemente debido a la inercia en torno al uso del retorno tipo void muchos fabricantes de compiladores agregan una extensin que permita el uso de cdigo sin modificaciones por lo que existen compiladores que soportan el retorno de tipo void pero el nico mtodo aprovado por el estndar ANSI-C es el de tipo int. Finalmente comprende Usted el motivo por el que el los programas que hemos estudiado le agregamos una lnea que retorna un valor de cero al sistema operativo, esto le indica que el programa se ejecut satisfactoriamente.

EL ALCANCE DE LAS VARIABLES


Dedicaremos una buena cantidad de tiempo en nuestro siguiente programa cubriendo algunos tpicos nuevos, algunos no parecen ser particularmente tiles sin embargo son muy importantes por lo que es conveniente estudiarlos detenidamente. Por el momento ignore los cuatro enunciados en las lneas 1 a 4 ya que las discutiremos mas adelante. # include <stdio.h> /* Prototipos de intrada/salida */ void head1(void); /* Prototipo para head1 */ void head2(void); /* Prototipo para head2 */ void head3(void); /* Prototipo para head3 */ int count; /* Una variable global */ int main() { register int index; /* Esta variable est disponible solo en main */ head1(); head2(); head3(); /* Bucle "for" principal de este programa */ for(index = 8 ; index > 0 ; index--) { int stuff; /* Esta variable est solo disponible para esta llaves */ for(stuff = 0 ; stuff <= 6 ; stuff++) printf("%d ", stuff); printf(" index es ahora %d\n", index);

76312839.doc } return 0; }

Pgina 20 de 59

HVF

int counter; /* Variable disponible a partir de este momento */ void head1(void) { int index; /* Esta variable est disponible solo en head1 */ index = 23; printf("El valor de header1 es %d\n", index); } void head2(void) { int count; /* Esta variable est disponible solo en head2 */ /* y desplaza a la variable global del mismo nombre */ count = 53; printf("El valor de header2 es %d\n", count); counter = 77; } void head3(void) { printf("El valor de header3 es %d\n", counter); } /* Resultado de la ejecucin: El valor de header1 es 23 El valor de header2 es 53 El valor de header3 es 77 0 1 2 3 4 5 6 index es ahora 8 0 1 2 3 4 5 6 index es ahora 7 0 1 2 3 4 5 6 index es ahora 6 0 1 2 3 4 5 6 index es ahora 5 0 1 2 3 4 5 6 index es ahora 4 0 1 2 3 4 5 6 index es ahora 3 0 1 2 3 4 5 6 index es ahora 2 0 1 2 3 4 5 6 index es ahora 1 */

QU ES UNA VARIABLE GLOBAL?


La variable definida en la lnea 6 denominada conut es global porque est disponible para cualquier funcin en el programa y est definida antes que cualquier otra funcin. Est siempre disponible porque existe durante todo el tiempo en que el programa es ejecutado. Mas adelante en el programa se define otra variable global llamada counter, es global pero no est disponible para la funcin main ( ) ya que est definida en seguida de la funcin main ( ). Una variable global es aquella que est definida fuera de cualquier funcin. Las variables globales son automticamente inicializadas a cero cuando son definidas, por lo tanto las variables count y counter tendrn ambas el valor de cero al ser inicializadas. Regrese a la funcin main ( ) y podr ver la variable index definida como de tipo int en la lnea 10, por el momento ignore la palabra register. Esta variable est solo disponible dentro de la funcin main ( ) porque es aqu en donde est definida, adems es una variable automtica, lo que significa que la variable existir cuando la funcin en la cual est contenida sea invocada y termina su existencia cuando la funcin finaliza. Otra variable de tipo entero llamada stuff est definida dentro de las llaves del bucle for. Cualquier par de llaves puede contener definiciones de variables que sern vlidas solo mientras el programa ejecuta los enunciados dentro de las llaves, por lo tanto, la variable stuff ser creada y destruida 8 veces, una por cada ciclo del bucle for.

76312839.doc

Pgina 21 de 59

HVF

Observe la funcin llamada head1 ( ) en la lnea 29. El uso de la palabra void lo explicaremos en breve. La funcin contiene una variable llamada index que no tiene nada en comn con la variable del mismo nombre de la funcin main ( ) en la lnea 10, excepto que ambas son variables automticas. Mientras el programa no ejecute sentencias de esta funcin esta variable no existir. Cuando head1 ( ) es llamada se genera la variable y cuando head1 ( ) termina su trabajo la variable llamada index de la funcin es eliminada por completo. Tenga en mente que esto no afecta la variable del mismo nombre en la funcin main ( ) porque se trata de entidades diferentes. Es importante recordar que de una llamada a la siguiente, el valor de una variable no se conserva y por lo tanto debe reinicializarse.

VARIABLES ESTTICAS
Al colocar la palabra clave static antes de la definicin de una variable dentro de una funcin, la las variables definidas son variables estticas y existirn de una llamada a otra en una particular funcin. Una variable esttica es inicializada una vez al cargar un programa y nunca es reinicializada durante la ejecucin del programa. Si colocamos la palabra clave static antes de una variable externa hacemos la variable privada lo que significa que esta variable no ser posible utilizarla con ningn otro archivo, ejemplos de esto se darn en el captulo 14.

UTILIZANDO EL MISMO NOMBRE


La funcin denominada head2 ( ) contiene la definicin de una variable llamada count. Aunque count ha sido definida como variable global en la lnea 6, es perfectamente vlido volver a utilizar el nombre en esta funcin pues se trata de una variable completamente nueva que nada tiene que ver con la variable global del mismo nombre ocasionando que la variable global no est disponible dentro de la funcin head2 ( ).

LA VARIABLE register
Una computadora puede almacenar datos en un registro o en memoria. Un registro es mucho mas rpido en operacin que una memoria paro hay pocos registros disponibles para uso del programador. Si en un programa existen ciertas variables que son utilizadas extensivamente, Usted puede designar que estas variables sean almacenadas en un registro para acelerar la ejecucin de un programa, esto se ilustra en la lnea 10. Su compilador probablemente le permita utilizar una o mas variables de registro, si su compilador no le permite el uso de este tipo de variables la peticin de registro ser ignorada.

PROTOTIPADO DE FUNCIONES
Un prototipo es un modelo de un objeto real y cuando Usted programa en ANSI-C, Usted tiene la habilidad para definir un modelo de cada funcin para el compilador. El compilador puede entonces usar el modelo para checar cada una de las llamadas a la funcin y determinar si Usted ha utilizado el nmero correcto de argumentos en la llamada a la funcin y si son del tipo correcto. El estndar ANSI-C contiene el prototipado como parte de sus recomendaciones, a lo largo de este estudio se tratar ampliamente el prototipado. Volviendo a las lneas 2, 3, y 4 del ejemplo que estamos estudiando, tenemos el prototipo para cada una de las tres funciones contenidas en el programa. El primer void le indica al compilador que esta funcin en particular no tiene valor de retorno. La palabra void dentro del parntesis le indica al compilador que esta funcin no tiene parmetros y si una variable fuera incluida ocurrira un error que el compilador indicara en un mensaje de advertencia. En este momento Usted empezar a utilizar el chequeo de prototipo para todas las funciones que Usted defina. La lnea 1 del programa le dice al sistema que obtenga una copia del archivo llamado stdio.h localizado en el directorio include. El archivo stdio.h contiene los prototipos para las funciones estndar de entrada/salida de tal manera que pueda ser posible checar los tipos adecuados de variables, mas adelante cubriremos en detalle el directorio include.

BIBLIOTECA ESTNDAR DE FUNCIONES


Cada compilador viene con una serie de funciones predefinidas disponibles para su uso, estas son en su mayora funciones de entrada/salida, de manipulacin de cadenas y caracteres y funciones matemticas. Los prototipos estn definidas para Usted por el escritor de su compilador para todas las funciones incluidas en su compilador. La mayora de los compiladores tienen funciones adicionales predefinidas que no son estndar pero que permiten al programador sacar mayor provecho de su computadora en particular, en el caso de las PC compatibles con IBM la mayora de estas funciones permiten utilizar los servicios de la BIOS en el sistema operativo o bien escribir directamente al monitor de video o en cualquier lugar de la memoria.

76312839.doc

Pgina 22 de 59

HVF

RECURSIVIDAD
La recursividad es otra de esas tecnicas de programacin que cuando las vemos por vez primera parecen muy intimidantes pero en el siguiente ejemplo descubriremos el misterio en un programa muy simple pero para propsitos de ilustracin resulta excelente. # include <stdio.h /* Contiene el prototipo para printf */ void count_dn(int count) ; /* Prototipo para count_dn */ int main( ) { int index ; index = 8 ; count_dn(index) ; return 0 ; } void count_dn(int count) { count -- ; printf ( "El valor de la cuenta es %d\n", count ) ; if (count > 0) count_dn(count) ; printf ( "Ahora la cuenta es %d\n", count ) ; } /* Resultado de la ejecucin: El valor de la cuenta es 7 El valor de la cuenta es 6 El valor de la cuenta es 5 El valor de la cuenta es 4 El valor de la cuenta es 3 El valor de la cuenta es 2 El valor de la cuenta es 1 El valor de la cuenta es 0 Ahora la cuenta es 0 Ahora la cuenta es 1 Ahora la cuenta es 2 Ahora la cuenta es 3 Ahora la cuenta es 4 Ahora la cuenta es 5 Ahora la cuenta es 6 Ahora la cuenta es 7 */ La recursividad no es otra cosa mas que una funcin que se llama a s misma, es por lo tanto un bucle que debe tener una manera de terminar. En el programa, la variable index es colocada en 8 en la lnea 8 y es utilizada como el argumento de la funcin llamada count_dn ( ). La funcin simplemente decrementa la variable, despliega un mensaje, y si la variable es mayor que cero, se llama a s misma donde decrementa la variable una vez ms, despliega un mensaje, etc, etc,etc. Finalmente la variable alcanza el valor de cero y la funcin ya no se llama a s misma, en lugar de esto retorna al punto previo a su llamada, y retorna de nueva cuenta, y de nuevo, hasta que finalmente retorna a la funcin main ( ) y de aqu retorna al sistema operativo. Para que le resulte mas claro piense como si tuviera ocho funciones llamadas count_dn disponible y que llama una a la vez manteniendo un registro de en cual copia estuvo en determinado momento, esto no es en realidad lo que sucede en el programa pero a manera de comparacin resulta til para comprender el funcionamiento del programa. Cuando Usted llama a la funcin desde la misma funcin, esta alamacena todas la variables y demas datos que necesita para completar la funcin en un bloque interno. La siguiente vez que es llamada la funcin hace exactamente lo mismo creando otro bloque interno, este ciclo se repite hasta alcanzar la ltima llamada a la funcin, entonces empieza a regresar los bloques utilizando estos para completar cada llamada de funcin. Los bloques son almacenados en una parte interna de la computadora llamada stack, esta es una parte de la memoria cuidadosamente organizada para almacenar datos de la manera ya descrita.

76312839.doc

Pgina 23 de 59

HVF

Al utilizar la recursividad es posible que Usted desee escribir un programa con recursividad indirecta, opuesta a la recursividad directa descrita arriba. La recursividad indirecta puede ser cuando una funcin A llama a una funcin B, la cual a su vez llama a la funcin A, etc. Esto es completamente permisible ya que el sistema tomar cuidado de almacenar en stack los datos para regresarlos cuando sea necesario. Recuerde que en la recursividad, en algn punto algo debe llegar a cero o alcanzar un punto predefinido para terminar el bucle. Si esto no es as, Usted tendr un bucle infinito, en determinado momento el stack se saturar resultando en un mensaje de error y terminando el programa abruptamente.

AYUDA A PROGRAMAR CON LIMPIEZA.


El preprocesador es un programa que se ejecuta justa antes de la ejecucin del compilador, su operacin es transparente para Usted pero hace un trabajo muy importante al remover todos los comentarios del cdigo fuente y efectuando una serie de sustituciones conceptuales basadas en su cdigo pasando el resultado al compilador. # include &ltstdio.h> # define INICIO 0 # define FINAL 9 # define MAX(A,B) ((A)>(B)?(A):(B)) # define MIN(A,B) ((A)>(B)?(B):(A)) int main( ) { int indice, mn, mx ; int contador = 5 ; for (indice = INICIO ; indice <= FINAL ; indice++) { mx = MAX(indice, contador) ; mn = MIN(indice, contador) ; printf ( "Max es %d y min es %d\n", mx, mn) ; } return 0 ; } /* Resultado de la ejecucin: Max es 5 y min es 0 Max es 5 y min es 1 Max es 5 y min es 2 Max es 5 y min es 3 Max es 5 y min es 4 Max es 5 y min es 5 Max es 6 y min es 5 Max es 7 y min es 5 Max es 8 y min es 5 Max es 9 y min es 5 */ Observe las lneas 3 a 6, cada una comienza con #define. Esta es la manera para declarar todas las macros y definiciones. Antes de iniciar el proceso de compilacin, el compilador v a la etapa del preprocesador para resolver todas las definiciones, en el presente caso, se buscar cada lugar en el programa donde se encuentre la palabra INICIO y ser reemplazada con un cero porque as est definido. El compilador en s jams ver la palabra INICIO. Observe que si la palabra se encuentra en una cadena o en un comentario, esta no ser cambiada. Debe quedarle claro que al poner la palabra INICIO en lugar del nmero 0 es solo por conveniencia para Usted actuando como comentario ya que la palabra INICIO ayuda a entender el uso del cero. Es una prctica comn en la programacin C utilizar letras maysculas para representar constantes simblicas y utilizar letras minsculas para los nombres de las variables. Usted puede utilizar el estilo de letra que mas le guste ya que esto es materia de gusto personal. /* Punto de inicio del bucle */ /* Fin del bucle */ /* Definicin macro de Max */ /* Definicin macro de Min */

76312839.doc

Pgina 24 de 59

HVF

QU ES UNA MACRO?
Una macro no es otra cosa que una definicin, pero como parece ser capaz de ejecutar algunas decisiones lgicas operaciones matemticas, tiene un nombre nico. En la lnea 5 del programa podemos ver un ejemplo de una macro, en este caso, cada vez que el preprocesador encuentra la palabra MAX seguida por un grupo de parntesis espera encontrar dos trminos en el parntesis y har el reemplazo de los trminos en la segunda parte de la definicin, as el primer trmino reemplazar cada A en la segunda parte de la definicin, y el segundo trmino reemplazar cada B en la segunda parte de la definicin. Cuando el programa alcanza la lnea 15, indice ser sustituda por cada A, y contador ser sustituida por cada B. Por lo tanto, antes de que la lnea 15 sea entregada al compilador, esta ser modificada por lo siguiente: mx = ((index)>(count) ? (index) : (count)) Recuerde que ni los comentarios ni las cadenas sern afectadas. Recordando las construcciones ya estudiadas vemos que mx recibir el valor mximo de indice contador. De la misma manera, la macro MIN resulta en mn recibiendo el valor mnimo de indice contador. Estas dos macros se utilizan con frecuencia en los programas C. Al definir una macro es imperativo que no haya espacio entre el nombre de la macro y el parntesis de apertura, de lo contrario, el compilador no podr determinar la existencia de una macro pero s har la sustitucin definida. Los resultados de la macro se imprimen en la lnea 17.

UNA MACRO EQUIVOCADA


En el siguiente cdigo podemos observar que la lnea 3 define una macro llamada EQUIVOCADA que aparentemente calcula el cubo de A, y en algunos casos lo hace, pero falla miserablemente en otros casos. La segunda macro llamada CUBO obtiene el cubo pero no en todos los casos, mas adelante estudiaremos el porque falla en algunas situaciones, el cdigo es el siguiente: #include &ltstdio.h> #define EQUIVOCADA(A) A*A*A #define CUBO(A) (A)*(A)*(A) #define CUADRADO(A) (A)*(A) #define SUMA_EQUIVOCADA(A) (A)+(A) #define SUMA_CORRECTA(A) ((A)+(A)) #define INICIO 1 #define FINAL 7 /* Macro EQUIVOCADA para el cubo */ /* Macro correcta para el cubo */ /* Macro correcta para el cuadrado */ /* Macro equivocada para la suma */ /* Macro correcta para la suma */

int main( ) { int i, offset ; offset = 5 ; for (i = INICIO ; i <= FINAL ; i++) { printf ("El cuadrado de %3d es %4d, y su cubo es %6d\n", i+offset, CUADRADO(i+offset), CUBO(i+offset)) ; printf ("El cubo equivocado de %3d es %6d\n", i+offset, EQUIVOCADA(i+offset)) ; } printf ("\nProbamos la macro de suma\n") ; for (i = INICIO ; i <= FINAL ; i++) { printf ("La macro de suma EQUIVOCADA = %6d, y la correcta = %6d\n", 5*SUMA_EQUIVOCADA(i), 5*SUMA_CORRECTA(i)) ; } return 0 ; } Considere el programa mismo donde el CUBO de i+offset se calcula en la lnea 19. Si i es 1, entonces estaremos buscando el cubo de 1+5=6, lo cual resulta en 216. Cuando se usa CUBO, los valores se agrupan as, (1+5)*(1+5)*(1+5)=6*6*6=216. Sin embargo, al utilizar EQUIVOCADA tenemos el siguiente agrupamiento, 1+5*1+5*1+5=1+5+5+5=16 lo que d un resultado errneo. Los parntesis son necesarios para agrupar adecuadamente las variables. En la lnea 6 definimos la macro SUMA_EQUIVOCADA de acuerdo a las reglas dadas pero an tenemos problemas cuando tratamos de utilizar esta macro en las lneas 27 y 28. En la lnea 28, cuando queremos que el programa calcule 5*SUMA_EQUIVOCADA(i) con i=1, obtenemos como resultado 5*1+1, lo que se evala como 5+1 6, y esto seguramente no

76312839.doc

Pgina 25 de 59

HVF

es lo que tenemos en mente, el resultado que realmente deseamos es 5*(1+1) = 5*2 = 10 que es la respuesta que obtenemos al utilizar la macro llamada SUMA_CORRECTA, esto se debe a los parntesis extra que agregamos en la definicin dada en la lnea 7. Dedicarle un poco de tiempo para estudiar este programa nos ayudar a comprender el funcionamiento de las macros. Para prevenir los problemas que hemos visto en el ejemplo, los programadores experimentados de C incluyen un parntesis en torno a cada variable en una macro y un parntesis adicional en torno a la totalidad de la expresin, esto permitir a cualquier macro trabajar adecuadamente y esta es la razn por la que la macro CUBO arroja ciertos resultados errneos, necesita un parntesis en torno a la expresin.

COMPILACIN CONDICIONAL. Parte 1.


Analizemos ahora el concepto de compilacin condicional en el cdigo siguiente. Se define OPCION_1 en la lnea 3, y se considera definida por el resto del programa, cuando el preprocesador alcanza la lnea 5 mantiene el texto comprendido entre las lneas 5 y 7 en el programa y lo pasa al compilador. Si OPCION_1 no hubiera sido definido en la lnea 5, el preprocesador se hubiera brincado la lnea 6 y el compilador jams la hubiera visto. Similarmente la lnea 17 es condicionalmente compilada siempre que OPCION_1 lo sea. Esta es una construccin muy til pero no en la manera en que la usamos en el ejemplo, generalmente se utiliza para inclur una caracterstica si estamos utilizando cierto tipo de procesador, o cierto tipo de sistema operativo o an una pieza especial de hardware. # include &ltstdio.h> # define OPCION_1 # ifdef OPCION_1 int contador_1 = 17; # endif /* Esto define el control del preprocesador */ /* Esto existe solo si OPCION_1 es definido */

int main( ) { int indice ; for (indice = 0 ; indice < 6 ; indice++) { printf ("En el bucle, indice = %d", indice) ; # ifdef OPCION_1 printf (" contador_1 = %d", contador_1) ; /* Esto puede ser desplegado */ # endif printf ("\n") ; } return 0 ; } # undef OPCION_1 /* Resultado de la ejecucin: (Con OPCION_1 definido) En el bucle, indice = 0 contador_1 = 17 En el bucle, indice = 1 contador_1 = 17 En el bucle, indice = 2 contador_1 = 17 En el bucle, indice = 3 contador_1 = 17 En el bucle, indice = 4 contador_1 = 17 En el bucle, indice = 5 contador_1 = 17 (Comentando removiendo la lnea 3) En el bucle, indice = 0 En el bucle, indice = 1 En el bucle, indice = 2 En el bucle, indice = 3 En el bucle, indice = 4 En el bucle, indice = 5 */

76312839.doc

Pgina 26 de 59

HVF

Compile y ejecute el programa como est, despus comente la lnea 3 de tal manera que OPCION_1 no sea definida entonces recompile y ejecute el programa, ver como la lnea extra no se imprimir porque el preprocesador se la brinc. En la lnea 25 ilustramos el comando al preprocesador undefine. Este remueve el hecho de que OPCION_1 fue definido y desde este punto el programa acta como si nunca hubiera sido definido, por supuesto que la instruccin undefine nada tiene que hacer en este punto del programa ya que ste est completo y no siguen mas enunciados ejecutables, como experimento coloque la instruccin undefine en la lnea 4, recompile y ejecute el programa y ver que acta como si OPCION_1 jams hubiera sido definido.

COMPILACIN CONDICIONAL. Parte 2.


En el siguiente programa ilustramos la directiva al preprocesador ifndef que se lee literalmente "si no definido". El programa de ejemplo siguiente representa un ejercicio real de lgica para el estudiante diligente y no debe representar problema alguno comprender el uso de la instruccin ifndef. # include &ltstdio.h> # define OPCION_1 # define MUESTRA_DATO # ifndef OPCION_1 int contador_1 = 17; # endif /* Esto define el control al preprocesador */ /* Si es definido, se muestra*/ /* Esto existe si OPCION_1 no es definido */

int main( ) { int indice ; # ifndef MUESTRA_DATO printf ("MUESTRA_DATO no est definido en " " el codigo\n") ; # endif for (indice = 0 ; indice < 6 ; indice++) { # ifdef MUESTRA_DATO printf ("En el bucle, indice = %d", indice) ; # ifndef OPCION_1 printf (" contador_1 = %d", contador_1); # endif printf ("\n") ; # endif } return 0 ; } /* Resultado de la ejecucin: (Con OPCION_1 definido) En el bucle, indice = 0 En el bucle, indice = 1 En el bucle, indice = 2 En el bucle, indice = 3 En el bucle, indice = 4 En el bucle, indice = 5 (Removiendo comentando la lnea 3) En el bucle, indice = 0 contador_1 = 17 En el bucle, indice = 1 contador_1 = 17 En el bucle, indice = 2 contador_1 = 17 En el bucle, indice = 3 contador_1 = 17 En el bucle, indice = 4 contador_1 = 17 En el bucle, indice = 5 contador_1 = 17 */

/* Esto puede mostrarse*/

76312839.doc

Pgina 27 de 59

HVF

COMPILACIN CONDICIONAL. Parte 3.


El siguiente programa ilustra un uso prctico del preprocesador. En este programa definimos un smbolo llamado EN_PROCESO, cuando llegamos al cdigo de la funcin main ( ) vemos el porque est definido. Aparentemente no tenemos suficiente informacin para completar este cdigo por lo que decidimos separar el cdigo hasta tener una oportunidad de hablar con Martn y Martha acerca de cmo completar estos clculos, mientras tanto deseamos continuar trabajando en otras partes del programa por lo que utilizamos el preprocesador para temporalmente brincarnos esta parte incompatible del cdigo, debido al mensaje que colocamos en la lnea 14 es imposible olvidar que debemos regresar y limpiar el cdigo. Veamos el ejemplo: # include &ltstdio.h> # define EN_PROCESO int main( ) { int indice ; for (indice = 0 ; indice < 6 ; indice++) { printf ("Indice es ahora %d", indice) ; printf (" y podemos procesar los datos") ; printf ("\n") ; # ifdef EN_PROCESO printf ("El codigo no ha sido completado! *************\n") ; # else for (contador = 1 ; contador < indice * 5 ; contador++) { vale = (ver la pag. 16 de la documentacin) limite = (Preguntar a Martin por este clculo) Martha tiene una tabla de datos para el anlisis del peor caso printf ("contador = %d, vale = %d, limite = %d\n, contador, vale, limite) ; } # endif } return 0 ; } /* Resultado de la ejecucin: (Con EN_PROCESO definido) Indice es ahora 0 y podemos procesar los datos. El codigo no ha sido completado! ************* Indice es ahora 1 y podemos procesar los datos. El codigo no ha sido completado! ************* Indice es ahora 2 y podemos procesar los datos. El codigo no ha sido completado! ************* Indice es ahora 3 y podemos procesar los datos. El codigo no ha sido completado! ************* Indice es ahora 4 y podemos procesar los datos. El codigo no ha sido completado! ************* Indice es ahora 5 y podemos procesar los datos. El codigo no ha sido completado! ************* (Removiendo comentando la lnea 3) (El programa no compilar por tener errores.) */ En este caso solo hemos tratado con unas cuantas lneas de cdigo. Podemos utilizar esta tcnica para manejar varios bloques de cdigo, algunos de los cuales pueden estar en otros mdulos, hasta que Martn regrese a explicar el anlisis y as poder completar los bloques indefinidos.

76312839.doc

Pgina 28 de 59

HVF

PROGRAMAS CON MLTIPLES ARCHIVOS


Para programas pequeos es conveniente incluir todo el cdigo en un solo archivo y compilarlo para obtener el resultado final, sin embargo, la gran mayora de los programas C son muy grandes para incluirlos en un solo archivo y trabajar cmodamente. Es normal encontrar un programa compuesto de varios archivos y es necesario para estos archivos comunicarse y trabajar juntos en un solo programa grande. Aunque es mejor no utilizar variables globales, algunas veces es conveniente su uso. Algunas de estas variables necesitan ser referenciadas por dos o mas archivos diferentes, C provee una manera de hacer esto. Considere las siguientes tres porciones de cdigo. Archivo1.c int indice ; extern int contador ; Archivo2.c extern int indice ; int contador ; Static int valor ; Archivo3.c extern int indice ;

int valor ; int main () ; static void uno () ; void dos () ; void tres () ; La variable llamada indice definida en Archivo1.c est disponible para utilizarse por cualquier otro archivo porque est definida globalmente. Los otros dos archivos hacen uso de la misma variable al declararla variable de tipo extern. En escencia se le est diciendo al compilador, "deseo utilizar la variable llamada indice la cual est definida en algn lugar". Cada vez que indice sea referenciada en los otros dos archivos, la variable de ese nombre es utilizada de Archivo1.c, y puede ser leda y modificada por cualquiera de los tres archivos, esto provee una manera fcil para intercambiar datos de un archivo a otro pero puede causar problemas. La variable llamada contador esta definida en Archivo2.c y esta referida en Archivo1.c como explicamos arriba, pero no puede utilizarse en Archivo3.c porque aqu no est declarada. Una variable esttica, como valor en Archivo2.c no puede ser referenciada por ningn otro archivo. Otra variable llamada valor est definida en Archivo3.c, esta no tiene ninguna relacin con la variable del mismo nombre en Archivo2.c. En este caso, Archivo1.c puede declarar una variable externa valor y hacer referencia a esta variable en Archivo3.c si se desea. El punto de entrada main ( ) solo puede ser llamado por el sistema operativo para iniciar el programa, pero las funciones dos ( ) y tres ( ) pueden ser llamadas desde cualquier punto dentro de los tres archivos ya que son funciones globales. Sin embargo, como la funcin uno ( ) esta declarada como de tipo esttica solo puede ser llamada dentro del archivo en la cual esta declarada.

QU ES UNA VARIABLE ENUMERADA?


Veamos en el siguiente cdigo un ejemplo de cmo utilizar la variable de tipo enum. # include &ltstdio.h> main() { enum {CERO,UNO,DOS,TRES,CUATRO=15,CINCO}numero; numero=CERO; printf("La primera variable numero de tipo enum es: %d\n", numero); numero=UNO; printf("La segunda variable numero de tipo enum es: %d\n", numero); numero=DOS; printf("La tercera variable numero de tipo enum es: %d\n", numero); numero=TRES; printf("La cuarta variable numero de tipo enum es: %d\n", numero); numero=CUATRO; printf("La quinta variable numero de tipo enum vale: %d\n", numero); numero=CINCO; printf("La ultima variable numero de tipo enum es: %d\n", numero); return 0; } La lnea 5 define una variable de tipo enum llamada numero. Esta variable puede tomar cualquier valor de los especificados dentro de las llaves. Si no se especifica un valor determinado, el sistema asigna automticamente valores enteros secuenciales empezando con cero, pero cuando se asigna un valor especfico, como es el caso de la variable CUATRO=15 entonces el siguiente valor enumerado ser de 16. Una variable de tipo enum es til cuando se manejan datos con una secuencia predeterminada, por

76312839.doc

Pgina 29 de 59

HVF

ejemplo los das de la semana, y de esta manera se puede manejar una sola variable la cual puede tomar cualquiera de sus valores predeterminados, mismos que pueden estar representados por nombres significativos. El resultado de la ejecucin del programa es el siguiente:

QU ES UNA CADENA DE CARACTERES?


En el caso especfico de la palabra inglesa "array" concerniente a este curso de C, no haremos la traduccin de la misma. En C, nos referimos a un array como un conjunto de datos todos del mismo tipo, siendo la cadena de caracteres un tipo especial de array pues se trata de un conjunto de datos de tipo char que termina con un caracter nulo, a este tipo de cadenas tambin se les conoce como "cadenas ASCII-Z" y ser la que trataremos en primer lugar. Empezamos por definir un array de tipo char y especificamos el tamao del mismo con un nmero, llamado subndice, encerrado entre corchetes. Este nmero le indica al sistema la cantidad de espacios para caracteres que contendr la cadena en cuestin. Los elementos de un array se almacenan en forma contigua en la memoria de la computadora y el subndice del primer elemento siempre es cero. El nombre del array es una constante que representa la direccin del primer elemento del array. Veamos un cdigo de ejemplo: #include <stdio.h int main() { char cadena[6]; /* Define una cadena de caracteres */ cadena[0]='L'; cadena[1]='e'; cadena[2]='t'; cadena[3]='r'; cadena[4]='a'; cadena[5]='s'; cadena[6]=0; /* Caracter nulo, significa el fin del texto */ printf("La cadena es %s\n", cadena); printf("La tercera letra de la cadena es: %c\n", cadena[2]); printf("Una parte de la cadena es : %s\n", &cadena[3]); return 0; } La variable cadena es por tanto una cadena que puede almacenar hasta seis caracteres, tomando en cuenta que se requiere un espacio para almacenar el caracter nulo al final de la cadena. El smbolo %s mostrado en los enunciados printf( ) le indica al sistema que despliegue una cadena de caracteres empezando con el elemento subndice cero, que en el cdigo de ejemplo es la letra L, y continuando hasta encontrar el caracter nulo. Observe que en los enunciados printf( ) cuando se indica la variable cadena sin corchetes indica que se despliegue la totalidad de la cadena, en tanto que al indicar la variable cadena con algn valor entre corchetes se refiere a un solo elemento de la cadena, en este caso debemos utilizar en el enunciado printf( ) el smbolo %c que le indica al sistema que despliegue un solo caracter. El smbolo & especifica la direccin en memoria de cadena[3], este smbolo lo estudiaremos mas adelante. Compile y ejecute el cdigo de ejemplo para mayor claridad en lo aqu expuesto. Modifiquemos nuestro cdigo para estudiar algunas funciones nuevas: #include <stdio.h #include <string.h int main() { char cadena1[17], cadena2[13], titulo[26], prueba[29]; strcpy(cadena1, "Pedro Picapiedra"); strcpy(cadena2, "Pablo Marmol"); strcpy(titulo, "- - -Los Picapiedra- - -"); printf("%s\n\n\n", titulo); printf("Los personajes principales son: %s\n", cadena1);

76312839.doc printf("y : %s\n\n", cadena2);

Pgina 30 de 59

HVF

if(strcmp(cadena1, cadena2) 0) strcpy(prueba, cadena1); else strcpy(prueba, cadena2); printf("La cadena mas grande es: %s\n\n", prueba); strcpy(prueba, cadena1); strcat(prueba, " y "); strcat(prueba, cadena2); printf("%s son vecinos\n", prueba); return 0; } Como puede ver, en este programa se han definido cuatro arrays de tipo char de diferente longitud, enseguida nos encontramos con la funcin strcpy( ) que sirve para copiar la cadena especificada en la segunda entidad dentro del parntesis de la funcin en un array de tipo char especificado por la primera entidad dentro del parntesis de la funcin strcpy, de esta forma, por ejemplo, la cadena "Pedro Picapiedra" se copia en el array de tipo char llamado cadena1. Mas adelante en el cdigo nos encontramos con la funcin strcmp( ) que como es fcil adivinar, sirve para comparar, letra por letra, dos cadenas especificadas dentro del parntesis. Esta funcin devuelve 1 si la primera cadena es mayor que la segunda, es decir, si tiene mayor cantidad de letras. Si ambas cadenas son iguales la funcin devuelve 0, en tanto que si la primera cadena es menor que la segunda entonces el valor devuelto es -1. Por ltimo tenemos la funcin strcat( ) que ejecuta una concatenacin de cadenas, es decir, copia la segunda cadena especificada dentro del parntesis de la funcin enseguida de la primera cadena especificada, agregando un caracter nulo al final de la cadena resultante. Naturalmente existen mas funciones para el manejo de cadenas, todas ellas fciles de implementar, lo mas recomendable en este caso es consultar la informacin de su compilador en particular.

ARRAYS DE TIPO int


Veamos ahora como trabajar con un array de tipo int en el siguiente programa que calcula la tabla del 5: #include <stdio.h int main() { static char titulo[]="Esta es la tabla del 5:"; int espacios[10]; int indice; for(indice=0; indice < 10; indice++) espacios[indice] = 5*(indice+1); printf("%s\n\n", titulo); for(indice=0; indice < 10; indice++) printf("5 x %2d = %4d\n", (indice+1), espacios[indice]); return 0; } Las primeras novedades las encontramos en la lnea 5 en donde podemos ver que hemos declarado un array de tipo char llamado titulo el cual no tiene especificado valor alguno dentro de los corchetes, esto se hace as para dejar que el sistema calcule el espacio necesario para la cadena especificada del lado derecho de la sentencia incluyendo el caracter nulo del final de la cadena, adems se ha declarado como static para evitar que el sistema asigne a la variable del array titulo como automtica y de esta manera se garantiza que la variable contenga la cadena especificada una vez que se ejecute el programa. En la siguiente lnea se declara un nuevo array de tipo int, es decir, tenemos aqu diez variables de tipo int llamadas espacios[0], espacios[1], espacios[2], etc. adems de una variable convencional de tipo int llamada indice. En primer lugar asignamos valores a cada uno de los elementos del array utilizando para ello un bucle for, mas adelante utilizamos

76312839.doc

Pgina 31 de 59

HVF

un segundo bucle para desplegar en orden cada uno de los valores almacenados en los elementos del array, el resultado de la ejecucin de este programa es el siguiente:

ARRAYS Y FUNCIONES
En la leccin Funciones mencionamos que haba una forma de obtener datos de una funcin utilizando un array, esto lo podemos ver en el cdigo siguiente en donde se ha definido un array de 15 variables llamado matriz, luego asignamos algunos datos a estas variables y desplegamos en pantalla las primeras cinco. En la lnea 17 llamamos a la funcin denominada una_funcion tomando todo el array como parmetro poniendo el nombre del array en el parntesis de la funcin. #include <stdio.h void una_funcion(int nombre_interno[]); int main() { int indice; int matriz[15]; for (indice = 0; indice < 15; indice++) matriz[indice] = indice + 1; for (indice = 0; indice < 5; indice++) printf("Valor inicial asignado a matriz[%d] = %d\n", indice, matriz[indice]); printf("\n"); una_funcion(matriz); /*Llama a la funcin denominada una_funcion*/

for (indice = 0; indice < 5; indice++) printf("Nuevo valor asignado a matriz[%d] = %d\n", indice, matriz[indice]); return 0; } void una_funcion(int nombre_interno[]) { int i; for (i = 0 ; i < 5; i++) printf("Valor de matriz[%d] al salir de la funcion= %d\n", i, nombre_interno[i]); printf("\n"); } La funcin una_funcion empieza en la lnea 25 y como se puede ver, prefiere llamar internamente a la matriz con el nombre de nombre_interno, es adems necesario declarar el array como de tipo int y especificar que se trata de un array incluyendo los corchetes, en este caso dejamos que el sistema determine el tamao del array al no especificar ningn valor entre los corchetes. Al regresar a la funcin principal main ( ) podemos comprobar lo que hemos dicho al desplegar los nuevos valores asignados a las variables del array denominado matriz. Otra forma de obtener datos de una funcin hacia el programa que la llama es utilizando un puntero, tema que estudiaremos en la siguiente leccin, ah veremos que el nombre de un array es en realidad un puntero hacia una lista de valores, pero antes de avanzar a la siguiente leccin veamos la manera de trabajar con arrays mltiples. Volver al principio

76312839.doc

Pgina 32 de 59

HVF

arrays MULTIPLES
Ya mencionamos que un array es un conjunto de datos almacenados en variables adyacentes en memoria, todos del mismo tipo. Siguiendo esta definicin podemos imaginarnos a un array como un conjunto de cajas apiladas una encima de la otra, de la misma manera podemos juntar dos o ms conjuntos de cajas apiladas unas encima de las otras en donde cada conjunto de cajas no es necesariamente del mismo nmero (tamao), se puede decir pues, que un array mltiple no es otra cosa que un array de arrays. En el cdigo de ejemplo generamos un array doblemente dimensionado. La variable multiplica es un array de 11 por 11 elementos, o sea un total de 121, el primer elemento es multiplica[0][0], y el ltimo es multiplica[11][11]. #include <stdio.h int main() { int i, j; int multiplica[11][11]; for (i = 0 ; i En este ejemplo se generan las diez tablas de multiplicar y se despliegan en pantalla en forma de matriz de 11 elementos para facilitar la comprensin del concepto. Por supuesto, es posible asignar valores a cada elemento del array en forma individual como queda demostrado en el cdigo del ejemplo que he modificado para que Usted lo compile y vea los resultados a manera de ejercicio: #include <stdio.h int main() { int i, j, valor1=8, valor2=9; int multiplica[11][11]; for (i = 0 ; i

Definicin
Dicho simplemente, un puntero es una direccin en memoria. Como es costumbre en este tutorial, los conceptos se explican mejor por s mismos, el cdigo es el siguiente: #include &ltstdio.h> main () { int almacen, *puntero; /* Una variable normal y un puntero de tipo int */ almacen=45; /* Se asigna un valor cualquiera a variable */ puntero=&almacen; /* La direccion de almacen */ printf("El contenido de la variable llamada almacen\n" "y que esta ubicada en %xh es de %d\n", puntero, *puntero); return 0; } /* Resultado de la ejecucin del programa: El contenido de la variable llamada almacen y que esta ubicada en 2796h es de 45 */

76312839.doc

Pgina 33 de 59

HVF

En primer lugar podemos ver una variable esttica llamada almacen y una ms que lleva un asterisco al principio llamada *puntero, por el momento no se fije en este detalle, lo explicaremos mas adelante. En la lnea 7 se le asigna a la variable almacen el valor de 45 tal y como lo hemos hecho en los programas vistos hasta ahora. En la lnea 8 se aprecia una forma de asignar un valor extrao a la variable llamada puntero, se trata del operador de direccin ampersand &, que se utiliza en C para acceder a la direccion en memoria de una variable, de aqu salen dos puntos muy importantes: 1. Cuando al nombre de una variable le precede el operador ampersand, ste define la direccin de la variable y por lo tanto se dice que apunta hacia la variable. En el cdigo de arriba se asigna a la variable llamada puntero la direccin de la variable llamada almacen. En efecto, la variable puntero es un puntero propiamente dicho. 2. Para saber el contenido de una variable sealada por un puntero utilizamos el asterisco antes del nombre de la variable puntero, en el cdigo de ejemplo observe en la instruccin printf( ) la manera en que se despliegan la direccin y el contenido de la variable llamada puntero. Como su nombre lo indica, decimos, refiriendonos a la lnea 4 del cdigo de ejemplo, que la direccin de memoria sealada por *puntero corresponde a una variable de tipo int, por lo tanto *puntero es un puntero a una variable de tipo int. Un puntero debe definirse para sealar a un tipo especfico de variable y por lo tanto no deber utilizarse en el mismo programa para sealar a una variable diferente pues esto produce errores de incompatibilidad de cdigo. Como se puede ver en el cdigo de arriba, es posible conocer el contenido de la variable almacen de dos formas diferentes, utilizando el nombre de la variable directamente, o bien con un puntero que seale a la direccin de almacen. Es comn en el estudio de punteros utilizar algunos grficos para comprender este importante tema de la programacin en C. Un rectngulo representa a la variable esttica almacen, en tanto que un rectngulo con un punto en su interior representa a un puntero, en nuestro ejemplo, el llamado a su vez puntero. Este diagrama representa el programa en el punto correspondiente a la lnea 5 en donde an no se le ha asignado valor alguno a las variables, observe que el puntero en este momento no apunta a ningn lado y la variable almacen no tiene asignado an un valor determinado. Siguiendo la ejecucin del programa, en la lnea 7 asignamos a almacen el valor de 45, en tanto que en la lnea 8 indicamos que puntero guarde la direccin de almacen. En la lnea 9 utilizamos la instruccin printf( ) para demostrar los dos importantes conceptos estudiados hasta ahora, en primer lugar indicamos desplegar el valor hexadecimal correspondiente a la direccin en memoria ocupada por la variable almacen y posteriormente desplegamos el valor almacenado en la variable en s, que en este caso es 45. Aunque en apariencia tenemos dos variables, en realidad se trata de una sola, en este caso almacen, solo que se hace uso del puntero para desplegar los valores mencionados. Es importante que observe detenidamente la instruccin printf( ) y que adems compile y ejecute el cdigo de ejemplo para un mejor entendimiento de estos conceptos.

Punteros y arrays
En el siguiente cdigo de ejemplo se han definido algunas variables y dos punteros. El primer puntero llamado alla es un puntero a una variable de tipo char y el segundo llamado pt apunta a una variable de tipo int. Tambin se han definido dos arrays llamados cadena y lista, los utilizaremos para demostrar la correspondencia entre punteros y los nombres de los arrays. El nuevo cdigo es el siguiente: #include &ltstdio.h> #include &ltstring.h> int main() { char cadena[30], *alla, primera, segunda; int *pt, lista[100], indice; strcpy(cadena, "Esta es una cadena de texto."); primera = cadena[0]; segunda = *cadena; /* primera y segunda son iguales */ printf("La primera salida es %c %c\n", primera, segunda); primera = cadena[8]; segunda = *(cadena+8); /* primera y segunda son iguales */ printf("La segunda salida es %c %c\n", primera, segunda);

76312839.doc

Pgina 34 de 59

HVF

alla = cadena+10; /* cadena+10 es igual a &cadena[10] */ printf("La tercera salida es %c\n", cadena[10]); printf("La cuarta salida es %c\n", *alla); for (indice = 0 ; indice < 100 ; indice++) lista[indice] = indice + 100; pt = lista + 27; printf("La quinta salida es %d\n", lista[27]); printf("La sexta salida es %d\n", *pt); return 0; } Utilizaremos un dibujo para representar la condicin inicial de nuestro programa. Se puede observar que tenemos tres variables, dos punteros, una cadena y un array de enteros, tambin podramos decir que tenemos tres variables, dos punteros y dos arrays. Cada array est compuesto por el array en s y un puntero que seala al inicio del array de acuerdo a la definicin de un array en C, esto quedar completamente aclarado en el siguiente prrafo. Cada array est compuesto de un nmero idntico de elementos de los cuales solo unos cuantos al principio y al final son mostrados para mayor claridad del dibujo. En C, el nombre de un array est definido como un puntero constante que seala al principio del array. En el cdigo de ejemplo se observa que en la lnea 8 asignamos una cadena constante a la variable llamada cadena simplemente para tener algunos datos con los cuales poder trabajar, enseguida asignamos a la variable de tipo char llamada primera el valor contenido en el primer elemento. Como el nombre de una cadena es un puntero constante al primer elemento de la cadena podemos asignarle el mismo valor a segunda utilizando el asterisco y el nombre de la cadena (*cadena). Tenga presente que en la lnea 8 sera incorrecto escribir segunda = *cadena[0]; porque el asterisco toma el lugar de los corchetes, o sea hacen el mismo trabajo. Para todo propsito prctico, cadena es un puntero a una variable de tipo char, esto tiene una restriccin que un puntero real no tiene, no puede ser cambiado como una variable ya que siempre contiene la direccin del primer elemento de la cadena y por lo tanto siempre apunta hacia el principio de la cadena. An y cuando no puede ser cambiado, se puede utilizar para referirse a otros elemntos de la cadena como veremos en la siguiente seccin del programa. En la lnea 14 se ha asignado a la variable primera el valor del noveno caracter de la cadena (recuerde que en C los ndices empiezan en 0) en tanto que a segunda se le asigna el mismo valor porque hemos permitido que el puntero seale ms all del principio de la cadena, en la lnea 15 dice que se debe sumar 8 al valor del puntero cadena, entonces obtener el valor almacenado en aquella locacin y almacenarlo en la variable segunda. Es muy recomendable que Usted compile y ejecute este segundo programa de ejemplo ya que adems de los conceptos tratados en los prrafos anteriores a su vez demuestra el concepto de la aritmtica de punteros, experimentar con el cdigo nos har ms familiar el manejo de los punteros y a la vez aportar nuevos elementos a lo ya visto en materia de arrays, es importante tener en cuenta que C maneja de forma automtica la organizacin de los punteros de acuerdo al tipo de variable que sealan, esto dependiendo a su vez de la forma en que el compilador defina los diferentes tipos de variables, este concepto quedar ms claro cuando en una leccin posterior estudiemos el concepto de las estructuras, por lo pronto estudie detenidamente la ltima parte del cdigo y observe como el sistema ajusta automticamente el ndice cuando utilizamos un puntero a una variable de tipo int. El resultado de la ejecucin del programa es el siguiente:

Punteros y funciones
Recordar que en la leccin Funciones mencionamos que haba dos maneras de obtener datos provenientes de una funcin. Una era a travs de un array y la otra es utilizando un puntero, demostramos este concepto en el siguiente programa de ejemplo: #include &ltstdio.h> void reparar(int tuercas, int *tornillos); int main() { int pernos, rondanas; pernos = 100; rondanas = 101; printf("Los valores iniciales son %d %d\n", pernos, rondanas); /* Cuando se llama a "reparar" */

76312839.doc

Pgina 35 de 59

HVF

reparar(pernos, &rondanas); /* tomamos el valor de pernos */ /* y la direccion de rondanas */ printf("Los valores finales son %d %d\n", pernos, rondanas); return 0; } void reparar(int tuercas, int *tornillos) /* tuercas es un valor entero */ /* tornillos apunta a un entero */ { printf("Los valores inicilales en la funcion son %d %d\n", tuercas, *tornillos); tuercas = 135; *tornillos = 172; printf("Ahora los valores en la funcion son %d %d\n" ,tuercas, *tornillos); } En este programa tenemos dos variables declaradas en el programa principal, pernos y rondanas, ninguna fu declarada como puntero. Enseguida asignamos valores a ambas y las desplegamos en pantalla y entonces llamamos a la funcin llamada reparar ( ) tomando ambos valores, a la variable pernos simplemente la enviamos como parmetro a la funcin, pero en cambio tomamos la direccin de la variable rondanas como segundo parmetro de la funcin. Ahora tenemos un problema. Los dos argumentos no son iguales ya que el segundo es un puntero a una variable. De alguna manera debemos alertarle a la funcin que recibir una variable entera y un puntero a una variable de tipo int, esto se hace de una manera simple. En la lnea 19 vemos que la funcin declara como primer parmetro a una variable de tipo int llamada tuercas y a un puntero a una variable de tipo int llamado tornillos, por tanto la llamada a la funcin en el programa principal est acorde con el encabezado de la misma. En el cuerpo de la funcin desplegamos los valores pasados como parmetros luego los modificamos y desplegamos los nuevos valores. Hasta este momento las cosas estn lo suficientemente claras, la sorpresa viene cuando regresamos a la funcin principal main ( ) y desplegamos los valores una vez ms. Encontramos que el valor de pernos se restaura al valor que tena antes de la llamada a la funcin reparar, esto es as porque C hace una copia de la variable en cuestin y lleva la copia a la funcin llamada, dejando a la variable original intacta tal y como lo explicamos anteriormente. En el caso de la variable rondanas hacemos una copia del puntero a la variable y llevamos la copia a la funcin. Como tenemos un puntero a la variable original, an y cuando el puntero es una copia local, sigue sealando a la variable original por lo tanto podemos cambiar el valor almacenado en rondanas desde el interior de la funcin reparar. Cuando regresamos al programa principal, encontramos a la variable rondanas con un nuevo valor. En el ejemplo no existe un puntero en el programa principal porque enviamos simplemente la direccin de la variable a la funcin reparar. El resultado de este programa es este:

Puntero a una funcin


En el siguiente programa mostramos como utilizar un puntero a una funcin, en la lnea 7 el programa define puntero_a_funcion como un puntero a una funcin y no solo a cualquier funcin, seala a una funcin con un solo parmetro de tipo float, la funcin adems no retorna nada, tal y como lo especifica la palabra clave void antes de la definicin del puntero. El parntesis en el nombre del puntero es necesario para evitar una posible confusin con una definicin de prototipo para una funcin que regrese un puntero a void. Este es el cdigo: #include &ltstdio.h> void despliega_cosas(float ignorar_datos); void despliega_mensaje(float mostrar_datos); void despliega_numero(float numero_flotante); void (*puntero_a_funcion)(float); int main() { float pi = 3.14159; float two_pi = 2.0 * pi; despliega_cosas(pi); /* Se muestra en pantalla */ puntero_a_funcion = despliega_cosas; puntero_a_funcion(pi); /* Se muestra en pantalla */ puntero_a_funcion = despliega_mensaje;

76312839.doc

Pgina 36 de 59

HVF

puntero_a_funcion(two_pi); /* Se muestra en pantalla */ puntero_a_funcion(13.0); /* Se muestra en pantalla */ puntero_a_funcion = despliega_numero; puntero_a_funcion(pi); /* Se muestra en pantalla */ despliega_numero(pi); /* Se muestra en pantalla */ return 0; } void despliega_cosas(float ignorar_datos) { printf("Esta es la funcion ignorar_datos\n"); } void despliega_mensaje(float mostrar_datos) { printf("El dato a mostrar es %f\n", mostrar_datos); } void despliega_numero(float numero_flotante) { printf("El numero a desplegar es %f\n", numero_flotante); } Observe los prototipos dados en las lineas 3 a 6 que declaran tres funciones que utilizan el mismo parmetro y regresa nada (void) al igual que el puntero. Como son similares, es posible utilizar el puntero para referirse a las funciones como lo demuestra la parte ejecutable del programa. En la lnea 14 contiene una llamada a la funcin despliega_cosas y la lnea 15 asigna el valor de despliega_cosas a puntero_a_funcion. Como el nombre de la funcin est definido como un puntero a esa funcin, su nombre puede ser asignado a una variable puntero hacia la funcin. Recordar que el nombre de un array es en realidad un puntero constante al primer elemento del array, de la misma manera, el nombre de una funcin es en realidad un puntero constante el cual seala a la funcin misma. El puntero es sucesivamente asignado a la direccin de cada una de las tres funciones y cada una es llamada una dos veces a manera de ilustracin de cmo se utiliza un puntero a una funcin. El resultado de la ejecucin del programa es el siguiente: Un puntero a una funcin no es utilizado a menudo pero es una construccin muy poderosa cuando se utiliza, continuaremos estudiando el uso de los punteros examinando los programas que veremos en las siguientes lecciones.

El archivo de cabecera stdio.h


Cuando nos referimos a entrada/salida estndar (E/S estndar) queremos decir que los datos o bien se estn leyendo del teclado, bien se estn escribiendo en el monitor de video. Como se utilizan muy frecuentemente se consideran como los dispositivos de E/S por default y no necesitan ser nombrados en las instrucciones de E/S. Esto le quedar claro a lo largo de este captulo. El primer cdigo es el siguiente: #include <stdio.h> int main() { int c; printf("Introduzca cualquier caracter y presione enter, X = termina el programa.\n"); do { c = getchar(); /* Toma un caracter del teclado */ putchar(c); /* Despliega el caracter en el monitor */ } while (c != 'X'); /* Mientras que sea diferente de X */ printf("\nFin del programa.\n"); return 0; /* Archivo de cabecera para entrada/salida estandar */

76312839.doc

Pgina 37 de 59

HVF

} La primera cosa por estudiar la tenemos en la primera lnea. La instruccin #include <stdio.h> es muy parecida a la instruccin #define que ya hemos estudiado excepto que en lugar de una simple sustitucin se lee todo un archivo en este punto. El sistema encontrar el archivo llamado stdio.h y leer la totalidad del contenido reemplazando la instruccin #include stdio.h. Obviamente el archivo debe contener enunciados vlidos de C que pueden ser compilados como parte del programa. Este archivo en particular contiene varias definiciones y prototipos para las operaciones de E/S estndar. El archivo es llamado archivo de cabecera y su compilador incluye una gran variedad de ellos, cada uno con un propsito especfico y cualquiera de ellos puede ser incluido en cualquier programa. El compilador C utiliza el smbolo de doble comilla para indicar que la bsqueda del archivo include empieza en el directorio actual de trabajo, y si no se encuentra ah, la bsqueda continuar en el directorio include tal y como est especificado en el ambiente de su compilador. Adems se utilizan los smbolos de "menor que" y "mayor que" para indicar que la bsqueda empieza directamente en el directorio include del compilador. Se pueden utilizar tantos include como sea necesario, y es perfectamente vlido que un archivo de cabecera incluya uno o ms archivos de cabecera adicionales. Comprender a su vez que cuando escriba programas largos, es posible incluir ciertas rutinas comunes en un archivo de cabecera e incluir el mismo como ya se ha descrito. Continuando con el cdigo de ejemplo. Se define la variable llamada c y se despliega un mensaje en pantalla con la y conocida funcin printf ( ), entramos en un bucle que no termina sino hasta que el caracter introducido sea una X mayscula, dentro del bucle nos encontramos con dos nuevas funciones, una que sirve para leer un caracter desde el teclado y otra que despliega dicho caracter en pantalla. La funcin getchar ( ) lee un solo caracter desde el dispositivo estndar de entrada, o sea, el teclado, y lo asigna a la variable llamada c. La siguiente funcin llamada putchar ( ) utiliza el dispositivo de salida estndar, es decir, el monitor de video, para desplegar el caracter contenido en c. El caracter se despliega en la posicin actual del cursor y ste avanza un espacio para el siguiente caracter, por lo tanto el sistema se ocupa del orden de despliegue de los caracteres. Compile y ejecute este programa para descubrir algunas caractersticas adicionales, como el hecho que conforme escriba en el teclado, lo escrito se despliega en el monitor y al presionar la tecla enter se repite la lnea completa de texto, tal parece que memoriza los caracteres y luego los vuelve a deplegar.

El sistema operativo a nuestra ayuda


Es conveniente dar una breve explicacin de cmo trabaja el sistema operativo para entender lo que pasa. Cuando se leen datos desde el teclado bajo el control del sistema operativo, los caracteres se almacenan en un buffer (segmento de memoria RAM temporal) hasta que se introduce un retorno de carro momento en el cual la totalidad de los caracteres se devuelven al programa. Cuando se teclean los caracteres, stos son mostrados en pantalla uno a la vez. A sto se le llama eco, y sucedeo en muchas de las aplicaciones que Usted corre en su computadora. Para demostrar lo dicho en el programa anterior, teclee una serie continua de caracteres tal que contenga una X mayscula, ver que conforme vaya tecleando la 'X' aparece en pantalla, pero una vez que presiona la tecla enter, la llegar la cadena de caracteres al punto donde se encuentra la 'X', ah termina el programa, lo que sucede es que al presionar la tecla enter se entrega al programa la cadena de caracteres, como el programa seala su fin al encontrar una X mayscula, los caracteres escritos despus de la 'X' no se despliegan en pantalla. Veamos otro cdigo: #include "stdio.h" #include "conio.h" int main() { int c; printf("Introduzca cualquier caracter, el programa termina con una X\n"); do { c = _getch(); /* Se obtiene un caracter */ putchar(c); /* Se despliega la tecla presionada */ } while (c != 'X'); printf("\nFin del programa.\n"); return 0; } Nuevamente empezamos con el archivo de E/S estndar de cabecera, luego definimos una variable llamada c e imprimimos un mensaje de bienvenida. Como en el programa anterior, entramos en un bucle y lo ejecutamos hasta encontrar una X mayscula, pero la accin es un poco diferente, observe la inclusin del archivo conio.h y de la funcin _getch ( ) mismos que no forman parte del estndar ANSI-C pero que estn disponibles en la mayora de los compiladores escritos para DOS. La funcin llamada _getch ( ) es una funcin para obtener un caracter que difiere de la funcin getchar ( ) en que no depende de DOS. sta funcin lee un caracter sin hacer eco en pantalla entregando el caracter inmediatamente al programa. Tenga en cuenta que la funcin _getch ( ) no

76312839.doc

Pgina 38 de 59

HVF

est incluida en el estndar ANSI-C y por lo tanto puede no estar disponible en todos los compiladores, adems utilizar sta funcin puede hacer el progama menos portable a otras mquinas, si su compilador soporta la funcin, compile este programa y observe su funcionamiento comparado con el programa anterior, ver que al presionar la tecla enter no coloca una nueva lnea con el retorno de carro, para corregir esta situacin tenemos el siguiente programa: #include <stdio.h> #include <conio.h> #define RC 13 #define AL 10 int main() { int c; printf("Introduzca cualquier caracter, X para terminar.\n"); do { c = _getch(); /* Se obtiene un caracter */ putchar(c); /* Despliega la tecla presionada */ if (c == RC) putchar(AL); /* Si es retorno de carro */ /* coloca una nueva linea */ } while (c != 'X'); printf("\nFin del programa.\n"); return 0; } Tenemos dos nuevos enunciados que definen los cdigos de caracter para la nueva lnea (linefeed en ingls, traducido en este programa como "alimentar lnea", AL), y para el retorno de carro (carriage return, "retorno de carro", RC), si Usted consulta una tabla de cdigos ASCII ver por qu stos trminos se han definido como 10 y 13. En el programa principal, despus de desplegar el caracter introducido en pantalla lo comparamos con RC, y si es igual adems desplegamos una nueva lnea, AL. En los programas presentados hasta este momento no hemos puesto, como es usual en este curso, las pantallas que demuestran la salida del programa, esto se debe al hecho de que los programas de este captulo depende su salida enteramente de lo que Usted teclee, por lo que le recomiendo ampliamente la compilacin y ejecucin de cada programa de ejemplo para un mejor entendimiento de la mecnica de las funciones aqu presentadas. Le toca ahora el turno a los nmeros enteros.

/* Define RC igual a 13 */ /* Define AL igual a 10 */

Entrada numrica
Estudie el siguiente programa: #include <stdio.h> int main() { int numero; printf("Introduzca un numero de 0 a 32767, el programa finaliza con un 100.\n"); do { scanf("%d", &numero); /* Lee un valor entero */ printf("El numero es %d\n", numero); } while (numero != 100); printf("Adios!\n"); return 0; } La mecnica del programa es bastante similar a lo que hemos estado trabajando, excepto que definimos una variable de tipo int llamada numero y el bucle continua hasta que el valor introducido sea 100. En lugar de leer un solo caracter tal y como lo hemos hecho en los programas anteriores, ahora leemos un nmero completo con una sola llamada a la funcin llamada scanf ( ), esta

76312839.doc

Pgina 39 de 59

HVF

funcin es muy similar a la conocida printf ( ) solo que se utiliza para introducir datos en lugar de desplegarlos. Observe en la lnea donde se encuentra la funcin scanf ( ) que sta no refiere directamente a la variable llamada numero, en lugar de esto, utiliza la direccin de la misma (o sea, un puntero a la variable), ya que espera le sea retornado un valor. La funcin scanf ( ) busca en la lnea de entrada hasta encontrar el primer campo de datos, lee los caracteres enteros hasta encontrar un espacio en blanco un caracter decimal invlido, en este punto detiene la lectura y retorna el valor encontrado. Si su sistema utiliza enteros de 2 bytes y Usted introduce un nmero hasta 32767 inclusive ste se despliega correctamente, pero con nmeros mayores parece haber un error. Por ejemplo, si Usted introduce 32768 se despliega -32768, e introduciendo 65536 el valor desplegado es cero. La explicacin de ste fenmeno est en la manera en que est definido una variable de tipo int, el bit ms significativo para un patrn disponible de 16 bits para una variable entera es el bit de signo por lo que slo nos quedan 15 bits para representar el valor, por lo tanto la variable slo puede tomar valores comprendidos entre -32768 y 32767. Este detalle debe tomarlo en cuenta al momento de hacer sus programas. Lo dicho es vlido slo para los compiladores de 16 bits, aunque existe la cada vez mayor posibilidad de que su compilador utilice valores enteros almacenados en campos mayores de 16 bits, en este caso se aplican los mismos principios excepto que el rango de valores es mayor. Compile y ejecute el programa anterior y experimente con lo anteriormente dicho.

Entrada de cadenas
Ahora veremos cmo introducir una cadena de caracteres en nuestro siguiente programa, el cdigo es el siguiente: #include <stdio.h> int main() { char cadena[25]; printf("Introduzca una cadena de caracteres, maximo 25 caracteres.\n"); printf("Una X en la columna 1 termina el programa.\n"); do { scanf("%s", cadena); printf("La cadena es -> %s\n", cadena); } while (cadena[0] != 'X'); printf("Adios!.\n"); return 0; } Este programa es similar al ltimo cdigo que estudiamos, excepto que en lugar de definir una variable de tipo int, definimos una variable de tipo string con un lmite de 24 caracteres (recuerde que las cadenas de caracteres deben incluir un caracter nulo al final de la cadena). La variable en la funcin scanf ( ) no requiere un smbolo de & porque cadena es un array y por definicin incluye un puntero. Este programa no requiere mayor explicacin. Cuando compile y ejecute ste programa notar que los enunciados son separados en palabras. Cuando scanf ( ) se utiliza en el modo de entrada de cadena de caracteres lee los caracteres hasta que encuentra el final de la linea un caracter en blanco, por lo tanto, lee una palabra a la vez. Experimente introduciendo ms de 24 caracteres y observe cmo el sistema maneja una situacin de error. Como scanf ( ) no tiene manera de parar la introduccin de caracteres cuando el array est lleno, por lo tanto no lo utilice para introducir cadenas de caracteres en un programa importante, aqu lo usamos solamente para propsitos de ilustracin.

Entrada/Salida en memoria
Hablemos ahora de otro tipo de E/S, uno que no tiene salida al mundo exterior pero que permanece en la computadora, el cdigo es este: #include <stdio.h> int main() { int numeros[5], resultado[5], indice; char linea[80];

76312839.doc

Pgina 40 de 59

HVF

numeros[0] = 74; numeros[1] = 18; numeros[2] = 33; numeros[3] = 30; numeros[4] = 97; sprintf(linea,"%d %d %d %d %d\n", numeros[0], numeros[1], numeros[2], numeros[3], numeros[4]); printf("%s", linea); sscanf(linea,"%d %d %d %d %d", &resultado[4], &resultado[3], (resultado+2), (resultado+1), resultado); for (indice = 0 ; indice < 5 ; indice++) printf("El resultado final es %d\n", resultado[indice]); return 0; } En este programa definimos algunas variables, despus asignamos algunos valores a las llamadas numeros para propsitos de ilustracin y entonces utilizamos la funcin sprintf ( ), sta acta similar a la funcin printf ( ) pero en lugar de desplegar los datos a un dispositivo de salida, imprime la cadena formateada en una cadena en memoria. En este caso la cadena v a la variable llamada linea, porque esta es la cadena que introducimos como primer argumento de la funcin sprintf ( ). Como la cadena generada contina en memoria, podemos leerla utilizando la funcin sscanf ( ), le decimos a la funcin en el primer argumento que linea es la cadena a utilizar para su entrada, el resto de los argumentos se manejan igual que en la funcin scanf ( ). Observe que en este caso si utilizamos punteros porque necesitamos regresar datos de la funcin y observe adems que utilizamos varias formas para declarar punteros, las primeras dos simplemente declaran la direccin de los elementos del array, mientras que los ltimos tres aprovechan el hecho que resultado, sin el subndice, es un puntero. Finalmente y para agregarle ms inters, los datos se despliegan en orden inverso.

Escritura de un archivo
A lo largo de sta leccin veremos la mecnica necesaria para escribir y leer datos a un archivo, empezaremos con la escritura. Como siempre, los cdigos especifican en primer lugar algunas sentencias #include, y en el caso concreto del primer cdigo de ejemplo se ha declarado un nuevo tipo de variable. Estudie el siguiente cdigo: #include <stdio.h> #include <string.h> int main() { FILE *fp; fp = fopen("prueba.htm", "w"); /* Abrir archivo para escritura */ fprintf(fp, "<HTML> \n"); fprintf(fp, "<BODY> \n"); fprintf(fp, "Esta es la primera linea de texto. \n"); fprintf(fp, "<CENTER>Esta es la segunda linea</CENTER> \n"); fprintf(fp, "Y esta es la <B>tercera linea de texto.</B> \n"); fclose(fp); /* Cerrar el archivo antes de terminar el programa */ printf("Se ha creado el archivo: prueba.htm \n"); return 0; } El tipo FILE es una estructura (misma que estudiaremos en la siguiente leccin) que est definida en el archivo de cabecera stdio.h, se usa para definir un puntero que se utilizar en operaciones con archivos. Por definicin, C requiere para accesar a un archivo de un puntero de tipo FILE, como es normal, se puede utilizar cualquier nombre para representar dicho puntero, es comn utilizar fp, as que ste nombre utilizamos en el primer cdigo.

76312839.doc

Pgina 41 de 59

HVF

Cmo abrir un archivo


Antes de poder escribir datos en un archivo, debemos abrirlo, esto significa que debemos decirle al sistema que deseamos escribir en un archivo especificando el nombre del mismo, para esto utilizamos la funcin fopen ( ), especificada en la lnea 8 del cdigo. El puntero de archivo, fp en ste caso, seala a la estructura para el archivo siendo necesarios dos argumentos para sta funcin, el nombre del archivo en primer lugar, y el atributo del archivo. El nombre del archivo es cualquier nombre vlido para su sistema operativo y puede ser expresado sea en minsculas maysculas, incluso si as lo desea, como una combinacin de mbas, el nombre se encierra entre comillas. En el ejemplo escog el nombre prueba.htm. Es importante que en el directorio donde trabaje stos ejemplos no exista un archivo con ste nombre pues al ejecutar el programa se sustituirn los datos del mismo, en caso de no existir un archivo con el nombre especificado, el programa lo crear.

Lectura ("r")
El segundo parmetro es el atributo del archivo y puede ser cualquiera de stas tres letras, "r", "w", "a", y deben estar en letra minscula. Existen atributos adicionales en C que permiten operaciones de Entrada/Salida (E/S) ms flexibles por lo que es recomendable la consulta de la documentacin del compilador. Cuando se utiliza "r" el archivo se abre para operaciones de lectura, para operaciones de escritura utilizamos "w" y cuando se especifica "a" es porque deseamos agregar datos adicionales a los ya existentes en el archivo, o sea concatenar datos. Abrir un archivo para lectura implica la existencia del mismo, si sta condicin no es vlida el puntero de archivo ser igual a NULL y sto puede ser verificado utilizando el siguiente cdigo: if (fp==NULL) { printf("Error al abrir el archivo \n"); exit (1); } Es una buena prctica de programacin checar todos los punteros de archivo en una forma similar al cdigo de arriba, el valor de 1 utilizado como parmetro de exit ( ) ser explicado ms adelante.

Escritura ("w")
Cuando un archivo se abre para operaciones de escritura, si ste no existe entonces ser creado, y si existe ser reescrito dando como resultado la prdida de los datos existentes en el archivo previo. Si ocurre por alguna razn un error al abrir el archivo, el puntero de archivo retorna un valor de NULL que puede ser checado como se especific arriba.

Concatenar ("a")
Cuando un archivo se abre para concatenar datos, si no existe ser creado inicialmente vaco. Si el archivo existe, el punto de entrada de datos se situa al final de los datos existentes en el archivo, de sta manera es como se agregan nuevos datos al archivo. El puntero de archivo se puede verificar como y se explic.

Salida al archivo
La salida de datos hacia un archivo es prcticamente idntica a la forma en que desplegamos datos en el dispositivo estndar de salida, las nicas diferencias reales son el nombre de una nueva funcin y la adicin del puntero de archivo como uno de los argumentos de la funcin. En el cdigo de ejemplo, la funcin fprintf ( ) reemplaza a la familiar printf ( ) y el puntero de archivo v como argumento dentro del parntesis de la funcin, como se aprecia en las lneas 9 a la 13 del cdigo de ejemplo.

Cerrando el archivo
Para cerrar un archivo se utiliza la funcin fclose ( ) con el puntero de archivo dentro del parntesis. En algunos programas sencillos no es necesario cerrar el archivo ya que el sistema operativo se encarga de cerrar los archivos que hayan quedado abiertos antes de retornar el control al usuario, sin embargo es buena prctica cerrar en cdigo todo aquel archivo que se abra. Compile y ejecute el programa, la nica salida que ver en pantalla es la lnea que indica la creacin del archivo especificado, despus de correr el programa verifique en su directorio de trabajo la existencia del archivo prueba.htm. Por la extensin utilizada es fcil suponer que se trata de un pequeo archivo web, su navegador lo puede visualizar de la forma convencional, pero tambin puede abrir ste archivo con un editor de texto comn (como Notepad), entonces se dar cuenta que el cdigo HTML est inconcluso, este "problemita" lo resolveremos ms adelante por lo que le recomiendo que conserve ste archivo pues se utilizar en las prcticas que siguen.

76312839.doc

Pgina 42 de 59

HVF

Concatenar datos
Como vimos en el programa anterior, el archivo generado llamado prueba.htm est inconcluso as que es hora de corregir sta situacin, lo haremos utilizando el cdigo que sigue el cual hace uso del atributo para concatenar datos y adems utilizaremos una nueva funcin para escribir en el archivo un solo dato a la vez: #include <stdio.h> #include <string.h> #include <stdlib.h> int main() { FILE *final; final = fopen("Prueba.htm", "a"); /* Abrir archivo para concatenar */ if (final == NULL) { printf("Falla al abrir el archivo \n"); exit (EXIT_FAILURE); } putc('\n', final); putc('<', final); putc('/', final); putc('B', final); putc('O', final); putc('D', final); putc('Y', final); putc('>', final); putc('\n', final); putc('<', final); putc('/', final); putc('H', final); putc('T', final); putc('M', final); putc('L', final); putc('>', final); putc('\n', final); fclose(final); return EXIT_SUCCESS; } En primer lugar observe que en este programa se efecta la verificacin del xito al abrir el archivo, la constante llamada EXIT_FAILURE est definida en el archivo de cabecera stdlib.h generalmente con el valor de 1. La constante llamda EXIT_SUCESS a su vez est definida generalmente con el valor de 0. El sistema operativo puede utilizar el valor retornado para determinar si el programa est operando normalmente si es necesario tomar alguna accin correctiva, por ejemplo, si un programa se ejecuta en dos partes y la primera de ellas retorna un valor de error, entonces no hay necesidad de ejecutar la segunda parte del programa.

La funcin putc ( )
La parte del programa que nos interesa es la funcin llamada putc ( ) ejemplificada de la lnea 16 a la 32, sta funcin extrae al archivo un caracter a la vez, el caracter en cuestin es el primer argumento de la funcin y el puntero de archivo el segundo y ltimo argumento dentro del parntesis. Observe que para especificar un caracter determinado se utiliza la comilla sencilla, incluyendo el caso del caracter de retorno de carro '\n'. Compile y ejecute el programa. Antes de correr el programa asegurese de la existencia del archivo prueba.htm en su directorio de trabajo, generado en el programa anterior, despus de correr el programa, abra el archivo con un editor de texto y observe que ahora el documento web est completo.

76312839.doc

Pgina 43 de 59

HVF

Lectura de un archivo
Como ya tenemos un archivo para leer podemos utilizar un nuevo programa, como en los programas anteriores, ste empieza con algunas declaraciones y abriendo el archivo prueba.htm especificando que deseamos efectuar operaciones de lectura mediante el atributo "r", el programa ejecuta un bucle do while para leer del archivo un slo caracter a la vez y desplegarlo en pantalla hasta detectar un caracter EOF (End Of File, Fin de Archivo). Por ltimo cerramos el archivo y el programa termina. #include <stdio.h> #include <stdlib.h> int main() { FILE *nombre; int c; nombre = fopen("Prueba.htm", "r"); if (nombre == NULL) { printf("El archivo no existe \n"); exit (EXIT_FAILURE); } else { do { c = getc(nombre); /* Obtiene un caracter del archivo */ putchar(c); /* Lo despliega en pantalla y continua... */ } while (c != EOF); /* hasta encontrar EOF (el final del archivo) */ } fclose(nombre); return EXIT_SUCCESS; } En este punto afrontamos un problema comn en programacin C. La variable regresada por la funcin getc ( ) es un caracter, por lo que podemos utilizar para el propsito una variable de tipo char, el problema empieza si tratamos de utilizar una variable de tipo unsigned char, ya que C regresa -1 para EOF. Una variable de tipo unsigned char no puede contener valores negativos ya que su rango est entre 0 y 255 por lo que retornar 255 para un valor negativo valor que no compara con EOF, en este caso el programa nunca terminar. Para prevenir esta situacin, utilice una variable de tipo int, ya que este tipo de variable siempre incluye el signo. En este programa leimos del archivo un caracter a la vez, en el siguiente leeremos una palabra a la vez.

Lectura de una palabra


El siguiente programa es prcticamente igual que el anterior excepto que sta vez se utiliza la funcin fscanf ( ) para leer una cadena a la vez, como fscanf ( ) detiene la lectura de caracteres al encontrar un caracter de espacio uno de nueva lnea, lee por lo tanto una palabra a la vez desplegando los resultados en una palabra por lnea, el nuevo cdigo es: #include <stdio.h> int main() { FILE *fp1; char palabra[100]; int c; fp1 = fopen("Prueba.htm", "r");

76312839.doc

Pgina 44 de 59

HVF

do { c = fscanf(fp1, "%s", palabra); /* Obtiene una palabra del archivo */ printf("%s\n", palabra); /* la despliega en pantalla */ } while (c != EOF); fclose(fp1); return 0; } Al ejecutar ste programa la salida es la siguiente: El problema es que se imprime dos veces la ltima palabra, para resolver este detalle modificamos el anterior cdigo as: #include <stdio.h> int main() { FILE *fp1; char palabra[100]; int c; fp1 = fopen("Prueba.htm", "r"); do { c = fscanf(fp1, "%s", palabra); /* Obtiene una palabra del archivo */ if (c != EOF) printf("%s\n", palabra); /* La despliega en pantalla */ } while (c != EOF); fclose(fp1); return 0; } Es bueno hacer notar que un programador experimentado no escribira el cdigo como lo hicimos en el ejemplo ya que compara c con EOF dos veces por cada ciclo del bucle y esto es ineficiente. Utilizamos cdigo que trabaja y es fcil de leer pero conforme Usted gane experiencia en C, Usted utilizar mtodos ms eficientes de codificar, aunque sean ms difciles de leer, por ejemplo: while((c = fscanf(fp1, "%s", palabra) != EOF) { printf("%s\n", palabra); } /* Se repite hasta encontrar EOF */ /* Se repite hasta encontrar EOF */

Lectura de una lnea


Siguiendo la misma secuencia de los ejemplos de ste captulo, analizaremos la forma de leer una lnea completa de texto, para esto tenemos el cdigo que detallo enseguida: #include <stdio.h> #include <stdlib.h> int main() { FILE *fp1; char palabra[100]; char *c;

76312839.doc

Pgina 45 de 59

HVF

fp1 = fopen("Prueba.htm", "r"); if (fp1 == NULL) { printf("Error al abrir el archivo \n"); exit (EXIT_FAILURE); } do { c = fgets(palabra, 100, fp1); /* Obtiene una linea del archivo */ if (c != NULL) printf("%s", palabra); /* La despliega en pantalla */ } while (c != NULL); fclose(fp1); return EXIT_SUCCESS; } Ahora utilizamos la funcin fgets ( ) la cual lee una lnea completa, incluyendo el caracter de nueva lnea y coloca los datos en un buffer (espacio de memoria RAM temporal). El buffer a ser ledo es el primer argumento en la llamada a la funcin en tanto que el mximo nmero de caracteres a ser ledos es el segundo argumento, seguido por el puntero de archivo. Esta funcin leer caracteres en el buffer hasta que encuentre el caracter de nueva lnea, lea el mximo nmero de caracteres menos uno, lo que ocurra primero. El espacio final se reserva para el caracter nulo (NULL) del fin de la cadena. Adems, si se encuentra un EOF, la funcin retorna NULL. NULL est definido a cero en el archivo stdio.h Los ejemplos de ste captulo realmente no requieren de mucha explicacin, el cdigo lo podemos modificar para introducir el nombre del archivo que deseamos abrir de sta forma: #include <stdio.h> #include <stdlib.h> int main() { FILE *fp1; char palabra[100], nombre[25]; char *c; printf("Introduzca el nombre del archivo -> "); scanf("%s", nombre); /* Lee el archivo deseado */ fp1 = fopen(nombre, "r"); if (fp1 == NULL) { printf("Error al abrir el archivo \n"); exit (EXIT_FAILURE); } do { c = fgets(palabra, 100, fp1); /* Obtiene una linea del archivo */ if (c != NULL) printf("%s", palabra); /* La despliega en pantalla */ } while (c != NULL); fclose(fp1); return EXIT_SUCCESS; } /* Hasta encontrar NULL */ /* Se repite hasta encontrar NULL */

76312839.doc La salida del programa es la siguiente:

Pgina 46 de 59

HVF

Asignacin especial
A lo largo de este captulo hemos tratado el uso de diferentes funciones para operaciones de lectura y escritura, se trata de un tema particularmente til en el desarrollo de un programa. Como seguramente habr notado, utilizamos para los ejemplos un archivo llamado Prueba.htm, por la extensin utilizada y por la naturaleza del texto includo en el mismo sabemos que se trata de un documento web que puede ser visualizado en su navegador. Para terminar este captulo y a manera de resumen que a la vez nos sirva de introduccin al siguiente captulo, le presento el siguiente cdigo que Yo espero despierte en Usted un poco ( un mucho) de curiosidad, experimente con el programa y si Usted desea, mndeme su opinin por correo electrnico. #include <stdio.h> #include <stdlib.h> enum HTMLid { HTML_NINGUNO, HTML_BODY, HTML_cBODY, HTML_B, HTML_cB, HTML_HTML, HTML_cHTML, HTML_CENTER, HTML_cCENTER }; static struct { char *htmlcodigo; enum HTMLid id; } lista_de_codigos[]= { {"<HTML>", HTML_HTML}, {"</HTML>", HTML_cHTML}, {"<BODY>", HTML_BODY}, {"</BODY>", HTML_cBODY}, {"<CENTER>", HTML_CENTER}, {"</CENTER>", HTML_cCENTER}, {"<B>", HTML_B}, {"</B>", HTML_cB}, {NULL, HTML_NINGUNO} }; char texto[128]; int itexto=0, c; int main() { int i, ietiqueta=0; char etiqueta[64]; FILE *archivo; enum HTMLid codigo; archivo = fopen("Prueba.htm", "r"); if (archivo == NULL) { /* Abre el archivo para lectura */

76312839.doc printf("El archivo no existe...\n"); exit (EXIT_FAILURE); } else { do {

Pgina 47 de 59

HVF

/* Checa todos los caracteres del archivo */ c=getc(archivo); if (c=='<') /* Lee la etiqueta html */ { etiqueta[ietiqueta++]=c; /* incluye el principio de la etiqueta */ do { c=getc(archivo); etiqueta[ietiqueta++]=c; } while(c!='>'); etiqueta[ietiqueta]=0; codigo=HTML_NINGUNO; for(i=0; lista_de_codigos[i].htmlcodigo!=NULL; i++) { if(stricmp(etiqueta, lista_de_codigos[i].htmlcodigo)==0) { codigo=lista_de_codigos[i].id; break; } } switch (codigo) { case HTML_NINGUNO: break; case HTML_HTML: printf("Empieza el documento web \n"); break; case HTML_cHTML: printf("Fin del documento web \n"); break; case HTML_B: printf("Empieza la etiqueta B \n"); break; case HTML_cB: printf("Termina la etiqueta B \n"); break; case HTML_BODY: printf("Empieza la etiqueta BODY \n"); break; case HTML_cBODY: printf("Termina la etiqueta BODY \n"); break; case HTML_CENTER: printf("Empieza la etiqueta CENTER \n"); break; case HTML_cCENTER: printf("Termina la etiqueta CENTER \n"); break; } ietiqueta=0; } else

76312839.doc rollo(); } while(c!=EOF); } fclose(archivo); texto[itexto]=0; printf(texto); return EXIT_SUCCESS; } rollo() { texto[itexto++]=c; } La salida del programa es la siguiente:

Pgina 48 de 59

HVF

Qu es una estructura?
Una estructura es un tipo de dato definido por el usuario, al utilizar una estructura Usted tiene la habilidad para definir un nuevo tipo de dato considerablemente ms complejo que los tipos que hemos utilizado hasta ahora. Una estructura es una combinacin de varios tipos de datos previamente definidos, incluyendo otras estructuras que hayamos definido previamente. Una definicin simple es, "una estructura es un grupo de datos relacionados en una forma conveniente al programador y/o al usuario del programa". Como es costumbre, un ejemplo nos clarifica los conceptos: #include <stdio.h> struct { char inicial; /* Letra inicial del apellido */ int edad; /* Edad */ int calificacion; /* Aprovechamiento */ } chico, chica; int main() { chico.inicial = 'R'; chico.edad = 15; chico.calificacion = 75; chica.edad = chico.edad - 1; /* Ella es un ao menor que l */ chica.calificacion = 82; chica.inicial = 'H'; printf("%c tiene %d anos y su calificacion es de %d\n", chica.inicial, chica.edad, chica.calificacion); printf("%c tiene %d anos y su calificacion es de %d\n", chico.inicial, chico.edad, chico.calificacion); return 0; } El programa empieza definiendo una estructura utilizando la palabra clave struct seguida de tres variables sencillas encerradas entre llaves, las cuales son los componentes de la estructura, despues de la llave de cierre tenemos enlistadas dos variables llamadas chico y chica. De acuerdo a la definicin de una estructura, chico es una variable compuesta de tres elementos, inicial, edad y, calificacion. Cada uno de los tres campos estn asociados a chico y cada uno almacena una variable de su respectivo tipo, lo mismo se puede decir para chica pero sus variables son diferentes por lo tanto tenemos 6 variables agrupadas en dos, de tipo struct.

76312839.doc

Pgina 49 de 59

HVF

Una variable compuesta


Examinemos la variable llamada chico ms carcanamente, como ya mencionamos, cada uno de los tres elementos de chico son simples variables y pueden ser utilizadas como cualquier otra, por ejemplo, el elemento edad es una variable de tipo int que puede ser utilizada en clculos, como contador, en operaciones de E/S, etc. Tenemos ahora el problema de definir cmo usar la variable llamada edad que es parte de la variable compuesta llamada chico, para esto utilizamos ambos nombres separados por un punto decimal con el nombre principal en primer trmino, entonces, chico.edad es el nombre completo para el campo edad de chico, este enunciado puede utilizarse en cualquier parte del programa C si deseamos referirnos a ste campo. De hecho, es ilegal utilizar el nombre chico edad individualmente porque son definiciones parciales de un campo.

Asignando valores a las variables


Usando la definicin dada arriba, podemos asignar un valor a cada uno de los tres campos de chico e igualmente para chica, observe que chico.inicial es una variable de tipo char ya que as fu definida en la estructura por lo que se le debe asignar un caracter. En la lnea 13 asignamos el caracter R a chico.inicial de acuerdo a las reglas en tanto que a los otros dos campos de chico se les asigna valores de acuerdo a sus respectivos tipos. Finalmente asignamos valores a los tres campos de chica pero en diferente orden para ilustrar que sto no es crtico, observe que se utiliza la edad del chico para determinar la edad de la chica, esto ilustra el uso de un miembro de la estructura.

Un array de estructuras
El siguiente programa es bsicamente el mismo que el anterior, pero esta vez definimos un array de 12 variables llamadas chicos, est claro que ste programa contiene 12 veces 3=36 variables sencillas cada una de las cuales puede almacenar un tem de dato siempre y cuando sea del tipo adecuado, se define adems una variable comn llamada indice para utilizarla en los bucles, estudie el cdigo: #include <stdio.h> struct { char inicial; int edad; int calificacion; } chicos[12]; int main() { int indice; for (indice = 0; indice < 12; indice++) { chicos[indice].inicial = 'A' + indice; chicos[indice].edad = 16; chicos[indice].calificacion = 84; } chicos[3].edad = chicos[5].edad = 17; chicos[2].calificacion = chicos[6].calificacion = 92; chicos[4].calificacion = 57; chicos[10] = chicos[4]; /* Asignacion de estructura solo en compiladores ANSI-C */

for (indice = 0; indice < 12; indice++) printf("%c tiene %d anos y una calificacion de %d\n", chicos[indice].inicial, chicos[indice].edad, chicos[indice].calificacion); return 0; }

76312839.doc

Pgina 50 de 59

HVF

Para asignar un valor a cada uno de los campos utilizamos un bucle for, en cada ciclo del bucle se asignan todos los valores para uno de los chicos, en una situacin real sta podra no ser la mejor manera de asignar datos, pero un bucle puede leer los datos de un archivo y almacenarlos en la correcta ubicacin en un programa real, considere ste ejemplo como un inicio burdo a una base da datos, pues eso es justamente nuestro ejemplo. El cdigo resulta fcil de entender, solo har un comentario respecto a la lnea 26 en donde podemos ver una asgnacin de estructura, en ste enunciado los tres campos de chicos[4] son copiados en los respectivos campos de chicos{10], esto no siempre est permitido en el lenguaje C, solo en los compiladores que cumplen con la norma ANSIC, si su compilador no es ANSI-C encierre en comentarios la lnea 26. El resultado de la ejecucin delprograma es el siguiente:

Estructuras y punteros
Ahora modificamos nuevamente el programa del ejemplo anterior para utilizar punteros en algunas de las operaciones, la primera diferencia se muestra en la definicin de las variables enseguida de la definicinde la estructura, tenemos un puntero llamado puntero el cual seala a la estructura, sera ilegal tratar de utilizar ste puntero para sealar a cualquier otro tipo de variable por una fuerte razn que estudiaremos un poco ms adelante en sta misma leccin, entre tanto le presento el cdigo: #include <stdio.h> struct { char inicial; int edad; int calificacion; } chicos[12], *puntero, extra; int main() { int indice; for (indice = 0; indice < 12; indice++) { puntero = chicos + indice; puntero->inicial = 'A' + indice; puntero->edad = 16; puntero->calificacion = 84; } chicos[3].edad = chicos[5].edad = 17; chicos[2].calificacion = chicos[6].calificacion = 92; chicos[4].calificacion = 57; for (indice = 0; indice < 12; indice++) { puntero = chicos + indice; printf("%c tiene %d anos y su calificacion es %d\n", (*puntero).inicial, chicos[indice].edad, puntero->calificacion); } extra = chicos[2]; extra = *puntero; /* Asignacion de estructura */ /* Asignacion de estructura */

return 0; } La siguiente diferencia la encontramos en el bucle for donde utilizamos el puntero para acceder a los campos de datos, recuerde que el nombre de un array es en realidad un puntero al primer elemento del array, como chicos es un puntero constante que seala al primer elemento del array el cual es una estructura, podemos definir a puntero en trminos de chicos. El elemento llamado chicos es constante por lo que no puede alterarse su valor, pero puntero es una variable puntero que se le puede asignar cualquier valor consistente requerido para sealar a la estructura. Si asignamos el valor de chicos a puntero entonces est claro que puntero

76312839.doc

Pgina 51 de 59

HVF

adems sealar al primer elemento del array que es una estructura que contiene tres campos. Es muy til para comprender el funcionamiento del programa utilizar un debbuger que nos permita ejecutar el programa paso a paso.

Aritmtica de punteros
Sumando 1 a puntero causar que seale al segundo campo del array debido a la manera en que los punteros son manejados en C. El sistema sabe que la estructura contiene tres variables y sabe cuntos elementos de memoria son requeridos para almacenar la estructura completa, por tanto si le indicamos al sistema que agregue 1 al puntero, agregar los elementos de memoria necesarios para obtener el siguiente elemento del array. Si, por ejemplo, sumamos 4 al puntero, el sistema avanzar el valor del puntero 4 veces el tamao de la estructura dando como resultado que el puntero seale 4 elementos ms all del array. Esta es la razn por la cual un puntero no puede utilizarse para sealar a otro tipo de dato excepto al que fu definido. Del prrafo anterior est claro que conforme avanzamos en el bucle el puntero sealar a uno de los elementos del array en cada ciclo, podemos por lo tanto utilizar el puntero para referenciar a varios elementos de cada una de las estructuras conforme avanzamos por el bucle. Referenciar a los elementos de una estructura con un puntero ocurre tan a menudo en C que se utiliza una notacin especial. Utilizar puntero->inicial es lo mismo que utilizar (*puntero).inicial lo cual es lo que hicimos en el programa. El smbolo de "->" se hace con el signo de menos y el de mayor que. Como el puntero seala a la estructura, debemos definir una vez ms cul de los elementos deseamos referenciar cada vez que utilizamos uno de los elementos de la estructura. Existen, como podemos ver, varios mtodos diferentes para referirnos a los miembros de la estructura. Cuando ejecutamos el bucle for para desplegar los datos al final del programa, utilizamos tres mtodos diferentes para referenciar los elementos de la estructura. Esto puede considerarse una prctica pobre de programacin pero la utilizamos para fines de ilustracin.

Estructuras anidadas
El siguiente ejemplo muestra una estructura anidada. Las estructuras que hemos visto han sido muy sencillas aunque tiles. Es posible definir estructuras conteniendo docenas y an cientos miles de elementos pero sera ventajoso para el programador no definir todos los elementos en una pasada sino utilizar una definicin de estructura jerrquica. #include <string.h> struct persona { char nombre[25]; int edad; char estado; /* C = casado, S = soltero */ }; struct datos { int calificacion; struct persona descripcion; char comida[25]; }; int main() { struct datos estudiante[53]; struct datos maestro, sub; maestro.calificacion = 94; maestro.descripcion.edad = 23; maestro.descripcion.estado = 'M'; strcpy(maestro.descripcion.nombre, "Lisseth Gil"); strcpy(maestro.comida, "Chocolates de Ron"); sub.descripcion.edad = 87; sub.descripcion.estado = 'M'; strcpy(sub.descripcion.nombre, "Abuela Pata"); sub.calificacion = 73;

76312839.doc strcpy(sub.comida, "Maiz y agua");

Pgina 52 de 59

HVF

estudiante[1].descripcion.edad = 15; estudiante[1].descripcion.estado = 'S'; strcpy(estudiante[1].descripcion.nombre, "Bill Griton"); strcpy(estudiante[1].comida, "Crema de cacahuate"); estudiante[1].calificacion = 77; estudiante[7].descripcion.edad = 14; estudiante[12].calificacion = 87; return 0; } La primera estructura contiene tres elementos pero no le sigue ninguna variable definida, slo una estructura, pero como incluimos un nombre al principio de la estructura, la estructura es llamada persona. La palabra persona puede utilizarse para referirse a la estructura pero no a cualquier variable de ste tipo de estructura, se trata por lo tanto de un nuevo tipo que hemos definido y lo podemos utilizar de la misma manera en que usamos un int, char o cualquier otro tipo que existe en C. La nica restriccin es que ste nuevo tipo debe estar siempre asociado con la palabra clave struct. La siguiente definicin de estructura contiene tres campos siendo el segundo la estructura previamente definida la cual llamamos persona. La variable de tipo persona se llama descripcion, as la nueva estructura contiene dos variables simples, calificacion y una cadena llamada comida, y la estructura llamada descripcion. Como descripcion contiene tres variables, la nueva estructura tiene entonces cinco variables, a sta estructura le hemos dado el nombre de datos, lo cual es otro tipo definido. Finalmente, dentro de la funcin main ( ) definimos un array de 53 variables cada una con la estructura definida por el tipo datos, y cada una con el nombre estudiante, en total hemos definido 53 veces 5 variables, cada una de las cuales es capaz de almacenar datos. Como tenemos la definicin de un nuevo tipo podemos utilizarla para a su vez definir dos variables. Las variables maestro y sub estn definidas en la lnea 20 como variables de tipo datos por lo que cada una de stas dos variables contienen 5 campos en los cuales podemos almacenar datos. En las lneas 22 a 26 del programa asignamos valores a cada uno de los campos de maestro. El primero es el campo calificacion y es manejado como las otras estructuras que hemos estudiado porque no forma parte de la estructura anidada. Enseguida deseamos asignar un valor a edad el cual es parte de la estructura anidada. Para acceder a ste campo empezamos con el nombre de la variable maestro al cual le concatenamos el nombre del grupo descripcion, y entonces debemos definir en cul campo de la estructura anidada estamos interesados por lo que concatenamos el nombre de la variable edad. El estado de los maestros se manejan de la misma manera que su edad pero los ltimos dos campos son cadenas asignadas utilizando la funcin strcpy ( ). Observe que los nombres de las variables en la funcin strcpy ( ) se consideran como una unidad aunque estn compuestas de varias partes. Compile y ejecute el programa, probablemente obtenga un aviso sea de error advertencia respecto a un desbordamiento de memoria similar a ste: Lo que sto significa es que el programa requiere ms memoria que la asignada por el compilador por lo que es necesario incrementar el tamao de stacks, el mtodo para hacer sto vara de un compilador a otro, en el caso concreto del compilador de Symantec que utilizo para los programas de ste tutorial, se cumple el objetivo asignando un modelo de memoria mayor:

Uniones
Dicho de una forma simple, una unin le permite manejar los mismos datos con diferentes tipos, utilizar el mismo dato con diferente nombre, a continuacin le presento un ejemplo: #include <stdio.h> int main() { union { int valor; /* Esta es la primera parte de la union */ struct { char primero; /* Esta es la segunda parte */ char segundo;

76312839.doc } mitad; } numero;

Pgina 53 de 59

HVF

long indice; for (indice = 12; indice < 300000L; indice += 35231L) { numero.valor = indice; printf("%8x %6x %6x\n", numero.valor, numero.mitad.primero, numero.mitad.segundo); } return 0; } En ste ejemplo tenemos dos elementos en la unin, la primera parte es el entero llamado valor el cual es almacenado en algn lugar de la memoria de la computadora como una variable de dos bytes. El segundo elemento est compuesto de dos variables de tipo char llamadas primero y segundo. Estas dos variables son almacenadas en la misma ubicacin de almacenamiento que valor porque sto es precisamente lo que una unin hace, le permite almacenar diferentes tipos de datos en la misma ubicacin fsica. En ste caso Usted puede poner un valor de tipo entero en valor y despus recobrarlo en dos partes utilizando primero y segundo, sta tcnica es utilizada a menudo para empaquetar bytes cuando, por ejemplo, combine bytes para utilizarlos en los registros del microprocesador. La unin no es utilizada frecuentemente y casi nunca por programadores principiantes, en este momento no necesita profundizar en el empleo de la unin as que no dedique mucho tiempo a su estudio, sin embargo no tome a la ligera el concepto de la unin, podra utilizarlo a menudo.

Qu es un campo de bits?
Para finalizar la presente leccin estudiaremos el concepto de campo de bits, en el siguiente cdigo podemos ver la manera de definirlo y utilizarlo, en el programa tenemos una unin de una variable sencilla de tipo int en la lnea 5 y la estructura definida en las lneas 6 a la 12: #include <stdio.h> union { int indice; struct { unsigned int x : 1; unsigned int y : 2; unsigned int z : 2; } bits; } numero; int main() { for (numero.indice = 0; numero.indice < 20; numero.indice++) { printf("indice = %3d, bits = %3d%3d%3d\n", numero.indice, numero.bits.z, numero.bits.y, numero.bits.x); } return 0; } La estructura est compuesta de tres campos de bits llamados x, y, y z. La variable llamada x es de un solo bit, la variable y es de dos bits y es adyacente a la variable x, y la variable z es de dos bits y adyacente a la variable y. Como la unin causa que los bits sean almacenados en la misma ubicacin en memoria que la variable indice, la variable x es el bit menos significante de la variable indice, y conforma los siguientes dos bits, y z es almacenado en los siguientes dos bits de indice. Compile y ejecute el programa y ver que al ser incrementada la variable indice en cada ciclo del bucle, ver los campos de bits incrementarse en sus respectivas

76312839.doc

Pgina 54 de 59

HVF

ubicaciones. Una cosa debemos sealar, los campos de bits deben definirse como partes de un tipo unsigned int de lo contrario el compilador marcar error. El resultado de la ejecucin del programa es:

Qu es la asignacin dinmica?
Hasta este punto del tutorial de C las variables que se han utilizado en los programas son de tipo estticas. (Algunas de ellas han sido automticas y fueron asignadas dinmicamente para Usted por el sistema pero esta operacin pas inadvertida para Usted). En este captulo estudiaremos algunas variables asignadas dinmicamente, stas son variables que no existen cuando se carga el programa pero se crean dinmicamente cuando son necesarias al correr el programa. Es posible, utilizando stas tcnicas crear tantas variables como sea necesario, utilizarlas y removerlas de su espacio en memoria para que sea utilizado por otras variables, como es costumbre, un ejemplo habla bin del concepto: #include <stdio.h> #include <string.h> #include <stdlib.h> struct animal { char nombre[25]; char raza[25]; int edad; } *mascota1, *mascota2, *mascota3; int main() { mascota1 = (struct animal *)malloc(sizeof(struct animal)); /* Es un error no checar la asignacion, consulte el texto. */ /* Checaremos la asignacion en el siguiente programa. */ strcpy(mascota1->nombre, "General"); strcpy(mascota1->raza, "Colicondela"); mascota1->edad = 1; mascota2 = mascota1; /* mascota2 ahora seala a la construccion de arriba */ mascota1 = (struct animal *)malloc(sizeof(struct animal)); strcpy(mascota1->nombre, "Francisco"); strcpy(mascota1->raza, "Labrador Cobrador"); mascota1->edad = 3; mascota3 = (struct animal *)malloc(sizeof(struct animal)); strcpy(mascota3->nombre, "Cristal"); strcpy(mascota3->raza, "Pastor Aleman"); mascota3->edad = 4; /* Desplegamos los datos */ printf("%s es un %s, y su edad es de %d.\n", mascota1->nombre, mascota1->raza, mascota1->edad); printf("%s es un %s, y su edad es de %d.\n", mascota2->nombre, mascota2->raza, mascota2->edad); printf("%s es un %s, y su edad es de %d.\n", mascota3->nombre, mascota3->raza, mascota3->edad); mascota1 = mascota3; /* mascota1 seala a la misma estructura que mascota3 */ free(mascota3); /* Esto libera una estructura */ free(mascota2); /* Esto libera otra estructura*/ /* free(mascota1); esto no se puede hacer, consulte el texto */ return 0;

76312839.doc

Pgina 55 de 59

HVF

} Empezamos definiendo una estructura llamada animal con algunos campos en relacin a unos perros, no definimos ninguna variable de ste tipo slo tres punteros, si Usted busca en el resto del cdigo del programa no encontrar ninguna variable definida por lo que no tenemos en donde almacenar datos, todo lo que tenemos para trabajar son tres punteros, cada uno de los cuales es capaz de sealar a variables de la estructura definida llamada animal. Para hacer cualquier cosa con el programa necesitamos algunas variables por lo que las crearemos dinmicamente

Creacin dinmica de variables


El enunciado en la lnea 15 asigna algo al puntero mascota1 crear una estructura dinmica conteniendo tres variables, el corazn del enunciado es la funcin malloc ( ) gravada en el centro del enunciado. Esta es una funcin para asignar memoria que requiere el resto del cdigo de la lnea para cumplir su objetivo. La funcin malloc ( ), por defecto, asignar una parte de memoria en una pila (heap) de "n" caracteres de longitud y ser de tipo char. La "n" debe especificarse como el nico argumento a la funcin, discutiremos "n" en breve, pero primero necesitamos definir la pila (heap).

Qu es la pila (heap)?
La pila (heap) es una rea predefinida de memoria que puede ser accesada por los programas para almacenar datos y variables, stos son asignados a la pila (heap) por el sistema cuando se realizan llamadas a malloc ( ). El sistema mantiene un registro del lugar donde los datos son almacenados, los datos y las variables pueden ser desasignadas al gusto dejando "agujeros" en la pila, el sistema sabe la ubicacin de los agujeros y los puede utilizar para almacenamiento adicional de datos con llamadas adicionales a malloc ( ), la estructura de la pila (heap) es por tanto una entidad en constante cambio, dinmica. Espero que la breve explicacin de la pila y la asignacin dinmica sea suficiente para entender lo que estamos haciendo con la funcin malloc ( ). Simplemente le solicita al sistema un bloque de memoria del tamao especificado regresando un puntero que seala al primer elemento del bloque, el nico argumento en el parntesis es el tamao deseado del bloque que en nuestro caso, deseamos un bloque que almacene una de las estructuras que definimos al principio del programa. El operador sizeof es nuevo, al menos para nosotros en ste curso, regresa el tamao en bytes del argumento entre parntesis, regresa por lo tanto, el tamao de la estructura llamada animal y se nmero es utilizado como parmetro en la llamada a malloc ( ), al completar sta llamada tenemos un bloque asignado en la memoria con el puntero llamado mascota1 sealando el principio de ste bloque.

Qu hacer si malloc ( ) falla?


Si no hay suficiente memoria disponible para el bloque solicitado, malloc ( ) no retorna un puntero vlido, en su lugar retorna el valor de NULL. El valor retornado siempre debe ser checado antes de intetar utilizarlo, en el ejemplo no realizamos prueba alguna por dos razones: en primer lugar y ms importante es el hecho de presentar los tpicos uno a la vez para facilitar el estudio, en segundo, estamos solicitando una pequea cantidad de memoria por lo que nuestro programa no causa problema. Sin embargo tenga siempre en cuenta que toda la memoria asignada dinmicamente debe ser cuidadosamente verificada.

Qu es el reparto (cast)?
Al principio de la llamada a la funcin malloc ( ) podemos observar el llamado reparto (Traduccin en ste tutorial del trmino en ingls "cast"). La funcin malloc ( ) retorna por defecto un puntero de tipo void, como no se puede utilizar un puntero que seale a nada, debe ser cambiado a otro tipo. Usted puede definir el tipo de puntero con la construccin dada en el ejemplo, en ste caso deseamos un puntero que seale a una estructura de tipo animal, as que se lo decimos al compilador con ste reparto. An en el caso de omitir el reparto, la mayora de los compiladores retornan un puntero correctamente dando un mensaje de advertencia y produciendo un ejecutable de todas maneras, es mejor prctica indicarle al compilador el reparto adecuado.

Utilizando la estructura asignada dinmicamente


En las leccines en estructuras y punteros vimos que si tenemos una estructura con un puntero sealandola podemos tener acceso a cualquier variable dentro de la estructura, en las lneas 18 a la 20 del programa asignamos algunos valores con propsito de ilustracin, observe que son similares a la asignacin de variables estticas. En la lnea 22 asignamos el valor de mascota1 a mascota2, esto no crea un nuevo dato, simplemente tenemos dos punteros al mismo objeto. Como mascota2 seala a la estructura creada para mascota1, sta puede ser utilizada de nueva cuenta para asignar dinmicamente otra estructura, lo mismo se puede decir para mascota2. A la nueva estructura le asignamos algunos valores para ilustracin en las lneas 24 a la 26. Finalmente asignamos datos a mascota3 de la misma manera y explicada y desplegamos los resultados en pantalla.

Desechando los datos asignados dinmicamente


Para desechar los datos asignados dinmicamente y liberar el espacio para su reutilizacin utilizamos la funcin free ( ), para sto simplemente llmela utilizando el puntero del bloque asignado dinmicamente como nico parmetro y el bloque es liberado. Para ilustrar otro aspecto de la asignacin dinmica y la liberacin de espacio, he incluido una etapa adicional en el programa, en la lnea 41 se le asigna a mascota1 el valor de mascota3, al hacer sto, el bloque que sealaba a mascota1 se pierde ya que no existe puntero que seale al bloque de memoria, por lo tanto no podemos referirlo, cambiarlo desasignarlo, ste bloque de memoria, el

76312839.doc

Pgina 56 de 59

HVF

cual es parte de la pila, se ha desperdiciado, sto no debe hacerse en un programa, lo mostramos a manera de ilustracin. La primera llamada a la funcin free ( ) remueve el bloque de datos sealado por mascota1 y mascota3 lo que desasigna la estructura y libera el espacio de memoria para su uso posterior. La segunda llamada a free ( ) remueve el bloque sealado por mascota2, en este punto del programa hemos perdido todo acceso a los datos generados al principio del programa.

Un array de punteros
Parece que la explicacin de la asignacin dinmica en el anterior ejemplo fu muy extensa, la buena noticia es que se ha dicho prcticamente todo al respecto, claro est que an falta por aprender algunas tcnicas en el uso de la asignacin dinmica y esto es lo que haremos en los siguientes ejemplos, empezando con el siguiente cdigo: #include <stdio.h> #include <string.h> #include <stdlib.h> struct animal { char nombre[25]; char raza[25]; int edad; } *mascota[12], *puntero; int main() { int indice; /* primero rellenar dinamicamente las estructuras con cualquier cosa */ for (indice = 0 ; indice < 12 ; indice++) { mascota[indice] = (struct animal *)malloc(sizeof(struct animal)); if (mascota[indice] == NULL) { printf("Falla en la asignacion de memoria.\n"); exit (EXIT_FAILURE); } strcpy(mascota[indice]->nombre, "Fido"); strcpy(mascota[indice]->raza, "Pastor para tacos"); mascota[indice]->edad = 4; } mascota[4]->edad = 12; mascota[5]->edad = 15; mascota[6]->edad = 10; /* estas lineas son para */ /* poner algunos datos */ /* en algunos de los campos */

/* Esto define 13 punteros, no variables */

/* Desplegamos los datos */ for (indice = 0 ; indice < 12 ; indice++) { puntero = mascota[indice]; printf("%s Es un %s, y tiene %d anos.\n", puntero->nombre, puntero->raza, puntero->edad); } /* es buena practica de programacion liberar el espacio */ /* dinamicamente asignado en el programa */ for (indice = 0 ; indice < 12 ; indice++) free(mascota[indice]);

76312839.doc

Pgina 57 de 59

HVF

return EXIT_SUCCESS; } Este programa es muy parecido al ejemplo anterior ya que se utiliza la misma estructura pero en el presente caso definimos un array de punteros para ilustrar los mecanismos para construir una base de datos grande utilizando un array de punteros en lugar de un puntero sencillo a cada elemento, para mantener el ejemplo sencillo definimos 12 elementos en el array y otro puntero adicional llamado puntero. El enunciado *mascota[12] es nuevo as que es conveniente hacer algunos comentarios. Lo que hemos definido es un array de 12 punteros siendo el primero mascota[0] y el ltimo mascota[11], como un array es en s un puntero, el nombre mascota es un puntero constante a otro puntero, sto es vlido en C, no hay limites en cuanto al nmero de niveles sealados, as, por ejemplo, la expresin int ****pt es legal ya que se trata de un puntero a un puntero a un puntero a un puntero a una variable de tipo entero, se recomienda tal uso de los punteros hasta haber ganado suficiente experiencia en su manejo. Ahora que tenemos 12 punteros los cuales pueden usarse como cualquier otro, es sencillo escribir un bucle para asignar dinmicamente un bloque de datos para cada puntero y rellenar los respectivos campos con los datos deseados, en ste caso rellenamos los espacios con datos de propsito ilustrativo pero bin podra tratarse de una base de datos, de informacin proveniente de algn equipo de prueba de laboratorio cualquier otra fuente de datos. Notar que en el ejemplo checamos cuidadosamente el valor retornado por la funcin malloc ( ) para verificar que no contenga un valor de cero, si retorna un valor NULL, desplegamos un mensaje indicandolo y terminando el programa, recuerde que en un programa real no basta con terminar el programa, es aconsejable generar un reporte de errores y darle al usuario la oportunidad de corregirlos y continuar el proceso iniciado antes de cerrar el programa. Volviendo al anlisis del cdigo, en las lneas 31 a la 33 escogimos algunos campos al azar para ilustrar el uso de enunciados sencillos y que los datos son desplegados en el monitor. El puntero puntero se utiliza en el bucle para desplegar datos slo para ilustracin, fcilmente se hubiera podido utilizar mascota[indice] en su lugar. Finalmente los 12 bloques son liberados de la memoria antes de terminar el programa, el resultado de la ejecucin es el siguiente, tenga en cuenta que mi compilador no maneja la letra ee:

Una lista enlazada


Finalmente llegamos a una de las tcnicas de programacin famosa por su intimidante nombre, una lista enlazada dinmicamente, con un poco de tiempo invertido en el estudio del cdigo se ver que no es otra cosa que una tcnica ms de programacin hecha de simples componentes y que puede ser una poderosa herramienta, antes de presentar el cdigo, considere sta analoga: es el da de su cumpleaos y su hermana le ha dado un presente, al abrirlo encuentra una nota que dice "Busca en el armario", Usted v al armario y encuentra otra nota que dice "Busca en la televisin", debajo de la televisin encuentra otra nota, "...debajo de la cafetera", Usted contina la bsqueda y finalmente encuentra un par de calcetines debajo del plato del perro. Lo que Usted hizo fu ejecutar una lista enlazada, el punto de inicio fu al abrir el regalo y termin debajo del plato de comida del perro, ah termin porque no haba ms notas. En nuestro programa haremos lo mismo que su hermana lo forz a hacer, pero ms rpido y dejaremos una pequea cantidad de datos en cada punto intermedio del camino a recorrer. Tenemos adems la posibilidad de regresar al principio y recorrer la lista completa una y otra vez si as lo deseamos. El cdigo de nuestro programa es ste: #include <stdio.h> #include <string.h> #include <stdlib.h> /* Necesario para definir NULL */

#define REGISTROS 6 struct animal { char nombre[25]; /* El nombre del animal */ char raza[25]; /* El tipo de animal */ int edad; /* La edad del animal */ struct animal *siguiente; /* seala a otra estructura de este tipo */ } *puntero, *inicio, *previo; /* Se definen tres punteros, no variables */ int indice; /* Una variable global */

int main() { /* El primer registro siempre es un caso especial */ inicio = (struct animal *)malloc(sizeof(struct animal)); if (inicio == NULL)

76312839.doc { printf("Falla en la asignacion de memoria\n"); exit (EXIT_FAILURE); } strcpy(inicio->nombre, "General"); strcpy(inicio->raza, "Pastor para tacos"); inicio->edad = 4; inicio->siguiente = NULL; previo = inicio;

Pgina 58 de 59

HVF

/* Una vez iniciada la lista, se puede utilizar un bucle para rellenar los bloques */ for (indice = 0 ; indice < REGISTROS ; indice++) { puntero = (struct animal *)malloc(sizeof(struct animal)); if (puntero == NULL) { printf("Falla en la asignacion de memoria\n"); exit (EXIT_FAILURE); } strcpy(puntero->nombre, "Pancho"); strcpy(puntero->raza, "Labrador"); puntero->edad = 3; previo->siguiente = puntero; /* seala al ultimo "siguiente" de este registro */ puntero->siguiente = NULL; /* seala este "siguiente" a NULL */ previo = puntero; /* Este es ahora el registro previo */ } /* Se despliegan los datos descritos */ puntero = inicio; do { previo = puntero->siguiente; printf("%s es un %s, y tiene %d anos de edad.\n", puntero->nombre, puntero->raza, puntero->edad); puntero = puntero->siguiente; } while (previo != NULL); /* Es buena practica liberar el espacio utilizado */ puntero = inicio; /* primer bloque del grupo */ do { previo = puntero->siguiente; /* siguiente bloque */ free(puntero); /* libera el actual bloque */ puntero = previo; /* seala al siguiente */ } while (previo != NULL); /* termina hasta que el siguiente sea NULL */ return EXIT_SUCCESS; } Este programa inicia de una manera similar a los ejemplos anteriores con la adicin de una declaracin constante para uso posterior, la estructura es casi la misma excepto por la adicin de otro campo dentro de la estructura en la lnea 12, se trata de un puntero a otra estructura del mismo tipo y ser utilizada para sealar a la siguiente estructura en orden, de acuerdo a la analoga de arriba, ste puntero sealar a la siguiente nota, que a su vez contendr un puntero a la siguiente nota. Definimos tres punteros y una variable para utilizarla como contador y con sto estamos listos para empezar a utilizar la estructura definida, una vez ms con datos sin sentido, slo para propsitos de ilustracin.

76312839.doc

Pgina 59 de 59

HVF

El primer campo
Utilizando la funcin malloc ( ) solicitamos un bloque de almacenamiento en memoria (en el heap) y lo rellenamos con datos, checando la correcta asignacin. Al campo adicional, en ste ejemplo llamado siguiente, se le asigna el valor de NULL, el cual es utilizado para indicar que ste es el final de la lista, dejaremos el puntero llamado inicio sealando a sta estructura de tal manera que siempre sealar a la primera estructura de la lista. Asignamos adems a previo el valor de inicio por razones que pronto veremos. Tenga en cuenta que los extremos de la lista enlazada sern siempre manejados de forma diferente a los elementos centrales de la lista, en ste punto tenemos un elemento en nuestra lista relleno de datos significativos.

Rellenando las estructuras adicionales


El siguiente grupo de enunciados de control y asignacin estn incluidos dentro de un bucle for de tal manera que podemos construir nuestra lista una vez que ha sido definida. El nmero de ciclos del bucle est determinado por la constante REGISTROS definida al principio del programa, en cada ciclo, asignamos memoria, hacemos la prueba de asignacin, rellenamos los primeros tres campos y los punteros. En el ltimo registro se le d a siguiente la direccin de ste nuevo registro porque el puntero llamado previo est sealando al registro previo, entonces previo->siguiente est dando la direccin del nuevo registro que hemos rellenado. En el nuevo registro se le asigna a siguiente el valor de NULL y al puntero previo se le d la direccin de ste nuevo registro porque la siguiente vez que generemos un nuevo registro, ste ser el previo en tal ocasin, aunque la explicacin est un poco confusa, estudiando el cdigo le resultar fcil entender la mecnica de asignacin. Cuando termina el bucle tenemos una lista de 7 estructuras, 6 correspondientes a la ejecuin del bucle y la estructura que generamos antes de iniciar el bucle, la lista tendr las siguientes caractersticas: El puntero llamado inicio seala a la primera estructura de la lista. Cada estructura contiene un puntero a la siguiente estructura. La ltima estructura tiene un puntero que contiene el valor de NULL el cual puede utilizarse para detectar el final de la lista. Debe quedar claro que no es posible simplemente brincar a mitad de la lista y cambiar algunos valores, la nica manera de acceder, por ejemplo, a la tercera estructura es iniciando por el principio y recorrer la lista una estructura a la vez, aunque parece un precio alto por la conveniencia de colocar algo de datos fuera del area del programa, es de hecho una buena forma de almacenar cierto tipo de datos. Para desplegar los datos se utiliza un mtodo similar al utilizado para generar los datos, los punteros son inicializados y entonces utilizados para avanzar de registro en registro, leyendo y desplegando cada registro a la vez. El despliegue termina cuando se encuentra un NULL, as que el programa no necesita saber siquiera cuntos registros hay en la lista. Finalmente borramos la lista completa para liberar espacio en memoria. Se debe tener cuidado de no borrar el ltimo registro antes de checar el valor NULL, ya que sera imposible terminar el programa. No es difcil ni tampoco trivial agregar elementos a mitad de una lista enlazada, es necesario crear un nuevo registro, rellenarlo de datos y sealar su puntero al registro que le preceder. Por ejemplo, si se desea agregar un nuevo registro entre los registros actuales tercero y cuarto, se requiere que el nuevo registro apunte hacia el cuarto registro, y el puntero del tercer registro debe sealar al registro recin creado. Agregar un nuevo registro al principio y al final de la lista son casos especiales, como ejercicio se le deja al lector considerar los pasos necesarios para agregar un nuevo registro en una lista doblemente enlazada. El resultado de la ejecucin del programa es ste:

Dos funciones ms
Debemos mencionar un par de funciones, calloc ( ) y realloc ( ). La funcin calloc ( ) asigna un bloque de memoria inicializado todo en ceros lo cual puede ser til en algunas circunstancias. Generalmente asignamos memoria e inmediatamente la rellenamos con datos as que es una prdida de tiempo rellenar primero conceros el bloque de memoria y despus con los datos del programa, por sta razn, la funcin calloc ( ) se utiliza muy raramente. por otra parte, la funcin realloc ( ) se utiliza para cambiar el tamao de un bloque asignado de memoria, a su vez es raramente utilizada, an por programadores experimentados.

Das könnte Ihnen auch gefallen