Você está na página 1de 67

Contenidos

Artculos
Programacin en C/Historia de C Programacin en C/Fundamentos de programacin Programacin en C/Primer programa en C Programacin en C/Tipos de datos Programacin en C/Interaccin con el usuario Programacin en C/Expresiones Programacin en C/Instrucciones de control Programacin en C/Uso de funciones Programacin en C/Vectores Programacin en C/Cadenas de caracteres Programacin en C/Manejo de archivos Programacin en C/Estructuras y Uniones Programacin en C/Punteros Programacin en C/Manejo dinmico de memoria 1 2 4 9 11 13 15 22 32 35 37 47 58 60

Referencias
Fuentes y contribuyentes del artculo 65

Licencias de artculos
Licencia 66

Programacin en C/Historia de C

Programacin en C/Historia de C
El lenguaje de programacin C fue creado por Dennis Ritchie entre 1969 y 1973 cuando trabajaba en Bell Laboratories de AT&T junto con Ken Thompson en el diseo del sistema operativo UNIX. C fue creado para poder escribir dicho sistema operativo en un lenguaje de alto nivel, independiente del hardware donde se ejecutara. Contar con un lenguaje de alto nivel permiti el avance de los sistemas operativos, ya que el mismo cdigo poda ser utilizado en las distintas plataformas, propiciando la reutilizacin de cdigo y reduciendo los tiempos de desarrollo. As es que los sistemas operativos basados en UNIX, el sistema BSD, el sistema GNU/Linux y muchos otros fueron desarrollados en C. Adems, con el paso del tiempo se han desarrollado cientos de bibliotecas que permiten a los programadores de C utilizar el cdigo desarrollado por otros para la realizacin de tareas comunes. Esto, a su vez, ha propiciado el desarrollo de aplicaciones en lenguaje C. Actualmente es imposible contar la cantidad de aplicaciones y herramientas desarrolladas en C.

Evolucin
A mediados de los aos 60s, Martin Richards dise el lenguaje BCPL con la finalidad de usarlo para escribir software de sistemas operativos y compiladores. En 1969, Ken Thompson escribi el Lenguaje B, en Bell Laboratories, con el objetivo de recodificar UNIX (escrito hasta ese momento en lenguaje ensamblador) usando un lenguaje de alto nivel ms portable y flexible. Durante los siguientes aos, Dennis Ritchie modific el lenguaje B, llegando a crear el lenguaje C y reescribiendo el sistema UNIX en dicho lenguaje; aadi caractersticas nuevas, como son el diseo de tipos y las estructuras de datos. En 1978, Dennis Ritchie y Brian Kernighan publicaron la primera edicin del libro El lenguaje de programacin C. Este libro fue durante aos la especificacin informal del lenguaje. El lenguaje descrito en la primera edicin de este libro, fue conocido como "el C de Kernighan y Ritchie" o simplemente "K&R C". En este libro se introdujeron nuevas caractersticas al lenguaje: los tipo de datos struct, long int y unsigned int; los operadores =+ y =- fueron sustituidos por += y -=. A mediados de los aos 80, Bjarne Stroustrup (tambin de los laboratorios Bell), crea el lenguaje C++, un lenguaje basado en C, con numerosas caractersticas adicionales, siendo la principial que est orientado a objetos. Si bien se han creado muchos lenguajes basados en C, C++ es el que ha permanecido ms asociado a C. En los aos siguientes a la publicacin del C de Kernighan y Ritchie, se aadieron al lenguaje muchas caractersticas no oficiales, que estaban presentes en algunos compiladores y no en otros. Fue por ello que en 1989 ANSI (American National Standards Institute) public el primer estndar oficial de C, que es conocido como ANSI C. En este estndar se tomaron muchas de las funcionalidades no oficiales y se agregaron funcionalidades nuevas como los prototipos de funcin, y un preprocesador mejorado. Tambin se cambi la sintaxis de la declaracin de parmetros de funciones, para que incluyeran el tipo junto con el nombre. Al ao siguiente, en 1990 se public la estandarizacin ISO del lenguaje. Este estndar es bsicamente el estndar ANSI, con unas pocas modificaciones de formato. A este estndar se lo conoce, entonces, como C89, o C90, y se trata del mismo lenguaje. Basndose en el estndar ANSI que estaba en preparacin, en 1988 Kernighan y Ritchie publicaron la segunda edicin de su libro, que es an hoy utilizada como una de las referencias principales del lenguaje. Durante los siguientes aos, el lenguaje C permaneci sin demasiados cambios. Sin embargo, como haba sucedido antes, los distintos compiladores fueron incorporando caractersticas adicionales, que otros compiladores no tenan, siendo C++ la principal influencia.

Programacin en C/Historia de C Fue por ello que a finales de los noventa se decidi revisar el estndar de C, lo que llev a la publicacin del estndar C99. Este estndar incluye varias nuevas caractersticas como son: las funciones inline; la posibilidad de declarar variables en cualquier parte del cdigo; los comentarios de una sola lnea utilizando //; los tipos de datos long long int, bool y complex, entre otras. An hoy el proceso de evolucin del lenguaje sigue avanzando, y desde 2007 se est trabajando en el armado de un nuevo estndar.

Ms informacin
Lenguaje de programacin BCPL Historia del lenguaje del programacin C The Development of the C Language [1]

Referencias
[1] http:/ / cm. bell-labs. com/ cm/ cs/ who/ dmr/ chist. html

Programacin en C/Fundamentos de programacin


En este captulo veremos un resumido listado de conceptos bsicos, esta informacin puede encontrarse en forma ms elaborada en el WikiLibro Fundamentos de programacin.

Definiciones
Se denomina algoritmo a una secuencia de instrucciones que permiten obtener un resultado en particular. No necesariamente son programas de computadora, una receta de cocina, o las instrucciones para cambiar un neumtico son ejemplos de algoritmos de la vida real. Las computadoras, son maquinas sin inteligencia propia, cuya nica finalidad es interpretar el cdigo que se les provee. El lenguaje de mquina es el nico lenguaje que la computadora "entiende" y es capaz de ejecutar. Los lenguajes de programacin son el medio de comunicacin entre el programador y una computadora. El programador escribe en algn lenguaje de programacin y utiliza las herramientas provistas por ese lenguaje para transformarlo en lenguaje de mquina. Finalmente, denominamos programa a una secuencia de rdenes a ser ejecutadas por una computadora. Un programa debe estar escrito en algn lenguaje de programacin, y puede incluir uno o ms algoritmos.

Tipos de lenguajes
Existe una gran cantidad de lenguajes de programacin, que estn pensados para distintas finalidades, siguen distintos paradigmas, y de una u otra forma se diferencian de los dems.

Esquemas de programacin
El esquema de programacin llamado Programacin Imperativa, consiste en escribir una secuencia de instrucciones una detrs de la otra, que se ejecutarn en orden. Algunas de esas instrucciones pueden hacer que la mquina pase a una instruccin que no sea la siguiente, tal vez porque se cumpla una condicin que hayamos

Programacin en C/Fundamentos de programacin establecido. En los ltimos aos ha tomado fuerza otro paradigma de computacin, llamado Programacin Orientada a Objetos , en el cual se intentan modelar los sistemas creados como extensiones de la realidad mediante la definicin de "objetos" que modelan entidades de la vida real y que interactan entre s mediante "mensajes" llamadas mtodos. El lenguaje C es un lenguaje imperativo, no orientado a objetos.

Alto o bajo nivel


Por otro lado, los lenguajes de programacin se clasifican en niveles. Un lenguaje es de ms bajo nivel cuanto ms cercano est al cdigo de mquina, y un lenguaje que es de ms alto nivel cuanto ms lejano est de la mquina y ms cercano al lenguaje humano. C es un lenguaje de alto nivel aunque tiene muchas caractersticas de lenguaje de bajo nivel (como el uso que permite hacer de la memoria). Estas caractersticas hacen que C sea un lenguaje muy potente, ya que permite optimizar al mximo los recursos de la mquina. Por ende, esto tambin hace que la dificultad y que los errores que se puedan cometer programando aumenten. As que a C se le considera de nivel medio. Lenguajes de ms alto nivel que C son aquellos en los que el programador no necesita encargarse de manipular la memoria, como Java, C#, Python, Ruby, entre otros.

Compilados o interpretados
Otra forma de clasificar a los lenguajes de programacin que es segn la forma en que se ejecutan sus rdenes. Existen los lenguajes que son interpretados, cuyas rdenes pasan a travs de un intrprete que se encarga de ejecutarlas (a partir del cdigo fuente) en el mismo momento en que estn siendo ledas. Algunos de los lenguajes interpretados son Python, Perl o Tcl, entre muchos otros. La contraparte de los lenguajes interpretados son los lenguajes compilados (como el mismo C) que se diferencian en que las rdenes son transformadas a lenguaje de mquina que se almacena en un archivo ejecutable. Ese archivo puede ejecutarse luego, sin recurrir al compilador. Los lenguajes compilados tienen la ventaja de la velocidad y la eficiencia, pero los interpretados tienen la ventaja de que, generalmente, son muy portables y de ms alto nivel.

Estructura de la memoria
Parte de esta potencia de C viene de que permite acceder con mucha libertad a la memoria de la mquina. Para entender un poco cmo es posible, debemos entender cmo se guardan los datos en la memoria. Imaginemos que la memoria tiene un montn de casillas, una enorme fila de casillas, cada una de las cuales contiene un dgito binario (bit): 0101001010100001010101001010000100111010110010010101001011010110001101010110101010110111... Es exactamente as, pero es ms cmodo recordar que esos bits se encuentran agrupados de ocho en ocho, formando octetos (bytes):
01010010 10100001 01010100 10100001 00111010 11001001 01010010 11010110 00110101 01101010 10110111 ...

Cada octeto puede contener y 255:

combinaciones distintas de ceros y unos, es decir, cualquier nmero entre 0

82 161 84 161 58 201 82 214 181 106 183 ...

Tambin podemos representar estos nmeros en base hexadecimal:

Programacin en C/Fundamentos de programacin

0x52 0xA1 0x54 0xA1 0x3A 0xC9 0x52 0xD6 0x35 0x6A 0xB7 ...

O considerarlos caracteres:
R T : R 5 j ...

Este es el tipo de dato ms elemental que nos podemos encontrar en C: el caracter. Un caracter ocupa exactamente un byte (8 bits) de memoria, y puede contener un nmero entre 0 y 255, o entre -128 y 127, dependiendo si queremos considerarlo como sin signo o con l.

Programacin en C/Primer programa en C


En el libro "El Lenguaje de Programacin C", Kernighan y Ritchie introdujeron al lenguaje C utilizando un sencillo programa que mostraba un saludo por la pantalla. Desde entonces se hizo tradicin empezar con cualquier lenguaje de programacin con el ejemplo del Hola mundo. En particular en C se involucran muchas partes y sintaxis del lenguaje, por lo cual es especialmente til verlo como el primer ejemplo de programacin en C. Ejemplo: Hola mundo /* Inclusin de archivos */ #include <stdio.h> /* Funcin principal */ int main (int argc,char **argv) { /* Impresin por pantalla y salida del programa*/ printf("Hola mundo\n"); return 0; } Para poder editar y ejecutar este programa ser necesario utilizar algn editor y luego un compilador, como se explic en la seccin Herramientas necesarias. Si se tiene el compilador gcc en un entorno UNIX o GNU/Linux, la forma sencilla de compilar y ejecutar ser: $ gcc holamundo.c $ ./a.out Hola Mundo $ Es decir que el compilador genera un archivo, en este caso llamado a.out, y la salida generada por ese archivo es "Hola mundo". A continuacin una explicacin detallada sobre el proceso de compilacin del programa, y luego un anlisis lnea por lnea del contenido de este ejemplo.

Programacin en C/Primer programa en C

Pre-requisitos para la compilacin de programas


Como ya se mencion, ser necesario tener instalado el compilador y un editor o entorno de desarrollo que permitan escribir el cdigo a compilar. Para ms informacin ver la seccin Herramientas necesarias. El cdigo a compilar debe guardarse con un nombre que represente al programa en cuestin y la extensin .c. En el caso del ejemplo del Hola mundo, el archivo puede llamarse hola.c. En las explicaciones a continuacin, se asume que se cuenta con un compilador instalado y se ha editado un archivo hola.c que se quiere compilar. Si tu sistema operativo no aparece en esta lista busca en internet, ya que seguro que existe algn compilador para ese sistema.

Compilacin de programas segn la plataforma


Windows
Para compilar un programa C en entornos Windows, debemos seguir una serie de pasos que varan segn el compilador de C que queramos utilizar. Antes que nada, sera bueno que se revises la documentacin del compilador elegido para conocer los comandos exactos. Compilacin del cdigo fuente Si se utiliza un entorno de desarrollo, ser posible compilar directamente desde el entorno, mediante un botn o una combinacin de teclas. Si se ejecuta el compilador desde la lnea de comandos, la lnea ser distinta segn el compilador utilizado. A continuacin algunos ejemplos de ciertos comandos segn el compilador: En Turbo C de Borland es: tcc hola.c En C++ de Borland: bcc hola.c En Visual C de Microsoft: cl hola.c En GNU gcc: gcc hola.c o cc hola.c El C de Zortech: ztc hola.c

Una vez compilado el cdigo fuente se genera un archivo llamado archivo objeto o programa objeto que es luego enlazado mediante el enlazador, para generar el archivo ejecutable. Los compiladores actuales suelen hacer dos funciones de una vez, compilando y enlazando todo en una sola funcin, aunque es posible pedirles que no lo hagan mediante parmetros adicionales. Segn el compilador y la configuracin utilizada, se obtendrn dos o tres archivos: El archivo fuente hola.c El archivo objeto hola.obj El archivo ejecutable hola.exe Este ltimo es el que nos interesa, puesto a que es el cdigo ejecutable, el programa en s. Al ejecutarlo se producir la salida deseada en una ventana de consola.

Programacin en C/Primer programa en C Salida por pantalla Si ejecutamos en entorno Windows el programa directamente desde el navegador de archivos, o tambin desde algunos entornos de desarrollo, lo que suceder ser que apenas abierta la ventana de la consola, se mostrar la cadena esperada y luego de terminada la funcin, la consola se cerrar sin tener el tiempo suficiente de ver nuestro mensaje en pantalla. Para poder ver la salida por pantalla ser necesario ejecutar el programa desde la lnea de comandos, o modificar la configuracin del entorno de desarrollo para que muestre la salida por pantalla al ejecutar el programa. La solucin a sto es agregar una funcin adicional a nuestro "hola.c": /* Inclusin de archivos */ #include <stdio.h> #include <stdlib.h> /* Funcin principal */ int main (int argc,char **argv) { /* Impresin por pantalla y salida del programa*/ printf("Hola mundo\n"); system ("pause"); return 0; } Las dos lneas agregadas permiten que utilicemos la biblioteca stdlib, que incluye la funcin system y que mediante esta funcin se ejecute el comando pause del sistema, que evita que el programa siga hasta que se presione una tecla. As es posible visualizar que la salida de hola.c se complet perfectamente.

Linux
Si bien existen otros compiladores, lo ms usual y ms sencillo para compilar un programa en GNU/Linux es el compilador gcc, ya que es el que se incluye en todas las distribuciones. De cualquier forma, es posible realizar la compilacin desde lnea de comandos o desde el entorno grfico. Para realizarla desde lnea de comandos, ser necesario contar con una terminal (xterm, konsole, gnome-terminal, etc). No es necesario contar con permisos de root para crear o compilar programas. En esa terminal ser necesario escribir gcc hola.c Si no existen errores en el cdigo, este comando nos crear un archivo ejecutable, que por omisin se llama "a.out", y que podemos ejecutar desde la lnea de comandos de la siguiente forma: ./a.out Hola mundo Es una buena idea especificar el nombre que el archivo ejecutable tendr, pasando como parmetro al compilador la opcin -o, de la siguiente forma: gcc hola.c -o hola Con lo cual, el nombre del archivo creado ser hola. Este archivo no tiene extensin ya que es la forma usual de llamar a los archivos ejecutables en los entornos UNIX y GNU/Linux, sin embargo funcionara de la misma forma si se llamara hola.exe.

Programacin en C/Primer programa en C Para ejecutarlo, haremos los mismo que en el caso anterior: ./hola Hola mundo Existen otros parmetros que podemos especificar al compilador en la lnea de comandos, dependiendo del tipo de programa, y en funcin de la complejidad del mismo. Por ejemplo, podemos agregar las siguientes opciones: gcc hola.c -o hola -Wall -pedantic La opcin -Wall nos mostrar todos los avisos que produzca el compilador, no solamente los errores. Los avisos nos indican dnde y/o porqu podra surgir algn error en nuestro programa. La opcin -pedantic nos aporta ms informacin sobre los errores y los avisos mostrados por GCC.

Diseccionando el "Hola Mundo"


A continuacin veremos cul es la estructura bsica de un programa en C, para poder entender qu hace cada una de las lneas de nuestro sencillo programa. Es probable que lo primero que salte a la vista sea la lnea: printf("Hola mundo\n"); Esta es la lnea que hace aparecer la cadena Hola Mundo en nuestra pantalla. Notamos que en C la sentencia para imprimir algo por pantalla es printf() y, adems, hay que colocar parntesis alrededor de lo que queremos imprimir para utilizarla. Esto se debe a que en C, printf es una funcin, que imprime su argumento (la cadena Hola Mundo\n) en la pantalla. Se denomina invocar una funcin a la accin de utilizarla para que realice una accin. Podemos observar tambin que la cadena a imprimir termina con una extraa combinacin: \n. La combinacin \n no representa a dos caracteres independientes, sino que representa un nico carcter no imprimible: el salto de lnea. Sin el salto de lnea, el resultado al ejecutar el programa sera: $ ./a.out Hola Mundo$ Es decir que no hay salto de lnea entre la cadena impresa, y la siguiente entrada de la lnea de rdenes, que no es lo que esperbamos. Lo ltimo a notar en la lnea es que termina con un punto y coma. En C, todas las sentencias terminan con un punto y coma. Al principio puede parecer obvio dnde termina una sentencia, pero ya veremos ms adelante que no lo es tanto. Observemos ahora la siguiente sentencia del programa: return 0; Luego de esta sentencia, termina el programa. Se trata de la instruccin return finaliza el programa, dicindole al sistema que el valor de retorno (el cdigo que el sistema operativo utiliza para saber si el programa ha funcionado bien o ha dado fallos) es 0, es decir, correcto. Estas dos sentencias se encuentran encerradas entre llaves. De esta manera, forman un bloque, es decir, un grupo de sentencias que se ejecutarn siempre de correlativa. Y qu es esa lnea que precede (en realidad, que da nombre) al bloque? Pues es la definicin de una funcin, la funcin main. En C (y en mucha de la programacin estructurada), todo se hace a base de funciones, como la arriba comentada printf. La funcin main es especial, porque es la que contiene el programa completo. Es decir, cuando el

Programacin en C/Primer programa en C ordenador va a ejecutar el programa, comienza a hacerlo por el principio de la funcin main, y cuando sta acaba, el programa tambin. La lnea: int main (int argc,char **argv) indica al compilador que la funcin que viene a continuacin, entre llaves, se llama main, cuyos argumentos son int argc y char **argv, que representan a la cantidad de argumentos ingresados al ejecutar el programa y a los valores de estos argumentos respectivamente, y da como resultado un nmero entero (int), que es el 0 de la instruccin return. Es importante sealar que el estndar dice que main deber definirse como funcin que retorna un entero, o de lo contrario el resultado queda indefinido. En un captulo posterior podr ver un ejemplo del uso de los parmetros que recibe main. Finalmente, y un tanto aparte (est separada del resto por una lnea en blanco), tenemos la lnea #include <stdio.h> Que parece bastante distinta al resto del programa, y que, adems, parece no tener sentido, puesto que ya hemos definido la funcin main que hace todo el trabajo. Efectivamente, esa lnea no es parte del programa, aunque sea imprescindible. La lnea es una instruccin del preprocesador de C, como nos lo indica el smbolo #, y lo que hace es incluir en ese punto el contenido de otro fichero, antes (de ah el nombre de preprocesador) de que comience la compilacin. El fichero stdio.h es el que contiene la definicin de la funcin printf(), que antes utilizamos pero que no escribimos, ya que forma parte de la biblioteca estndar de C.

Comentarios
Una vez escrito un cdigo, tratar de entenderlo un ao ms tarde solo con leerlo puede ser frustrante: no hay manera de saber (si el programa es medianamente complicado) qu es cada variable, o qu hace cada bloque de cdigo. Por esto, en cualquier lenguaje de programacin son importantes los comentarios. Muchas veces, se trabaja en equipo y no todos programan de la misma manera, aqu los comentarios son fundamentales. Un comentario en C es todo lo que se encuentre entre los smbolos /* y */. Hay que tener en cuenta que los comentarios no se pueden anidar: si dentro de un comentario hay un /*, seguir siendo el primer */ el que finalice el comentario, no se esperar al segundo. Hay otro tipo de comentarios en C, procedentes del lenguaje C++, e incorporadas al estndar de C a partir de C99: //. Todo lo que est despus de estos signos, hasta el final de la lnea, se considerar un comentario y el compilador no lo tomar en cuenta. En el ejemplo presentado pueden verse tres lneas con comentarios, que documentan someramente las distintas funcionalidades del cdigo. En los prximos captulos podrn verse mejores usos de los comentarios dentro del cdigo.

Programacin en C/Tipos de datos

Programacin en C/Tipos de datos


Historia
En el lenguaje C estandarizado como C89, existan cuatro tipos de datos bsicos que son: los nmeros enteros, los nmeros reales, los caracteres, y los punteros. A partir del estndar C99 se agregan: los valores lgicos (verdadero o falso) y los nmeros complejos. Estos tipos de datos son parte del lenguaje, y por ello se los considera primitivos. Ms adelante veremos que con el uso de estructuras y uniones es posible crear tipos compuestos de datos a partir de estos tipos primitivos. En este captulo veremos los enteros, los reales y los caracteres. Ms adelante se vern otros tipos de datos ms complejos, como son los vectores, las cadenas de caracteres, y los punteros en general.

Enteros
Los enteros son el tipo de dato ms primitivo en C. Se usan para representar nmeros enteros. Pero siempre se pueden encontrar otras aplicaciones para los nmeros enteros. En general se pueden usar para representar cualquier variable discreta. Los tipos de datos enteros son: short, int, long y long long. Es decir que para el lenguaje C existen diferentes tamaos de nmeros enteros que, segn el compilador y la plataforma de hardware, pueden tener desde 1 byte hasta 8 bytes (para ms detalles busca en la referencia). Adems, el lenguaje C hace la distincin de si el entero es con signo o sin signo (signed o unsigned). La forma de declarar un entero es con uno de los tipos de datos que sean enteros segn el tamao que se quiera. En caso de que no se declare si es con signo o sin signo, se toma con signo. Algunos ejemplos de declaraciones de enteros: int a; unsigned int a; signed long a; signed long long a = 10000000; Todos los nmeros son representados en memoria mediante una cadena de bits. En el caso de los nmeros con signo, el bit ms significativo es el que se usa para representar el signo. La representacin de los nmeros negativos se realiza mediante el complemento a dos, que es una tcnica que permite operar con los nmeros negativos de forma lgica. Slo a modo de ejemplo, la representacin en memoria de un -8 en una variable de 2 bytes, entera, con signo sera la siguiente: 1111111111111000 Ntese que no se ha tenido en cuenta el endianness de la arquitectura.

Programacin en C/Tipos de datos

10

Flotantes
Se denomina flotantes a los tipos de datos que representan a los nmeros reales, ya que utilizan un sistema de representacin basado en la tcnica de coma flotante, que permite operar con nmeros reales de diversas magnitudes, mediante un nmero decimal llamado mantisa y un exponente que indica el orden de magnitud. El tipo de dato flotante en lenguaje C slo tiene dos tamaos: el float y el double, que son 4 bytes y 8 bytes respectivamente. Se los puede utilizar tanto para representar nmeros decimales, como para representar nmeros enteros con un orden de magnitud muy grande. La forma de declarar una variable flotante es escribiendo en una lnea uno de los tipos de datos flotantes y a continuacin el nombre de la variable y tal vez algn valor que se les quiera dar. Algunos ejemplos: float a; double a = 1e23; double a = 3.1416; float a = 4e-9; double a = -78; Hay que tener en cuenta que aunque los valores flotantes son ms convenientes para algunas aplicaciones, hay casos en los que se prefieren los enteros. Esto se debe a que los nmeros flotantes no necesariamente tienen soporte de hardware, en particular en las plataformas integradas. Una alternativa que se utiliza en estas situaciones es interpretar los enteros como decimales de forma que 150 se interprete como 1.5 y 2345 como 23.45. Para el caso de los flotantes de 4 bytes, se utiliza 1 bit para el signo, 8 bits para el exponente y 23 bits para el valor del nmero. El procedimiento para almacenar un nmero en una variable flotante es el siguiente: 1. Se convierte a binario la parte entera. 2. Se coloca el signo en el bit ms significativo de la misma manera que en los enteros (1 para el - y 0 para el +). 3. Se mueve la coma (en la representacin binaria de la parte entera) hasta que est a la derecha del primer uno y ste se descarta (el uno ms significativo). El valor del exponente ser el nmero de posiciones que se movi la coma. El exponente usa la representacin de un entero con complemento a dos. 4. Se convierte en binario la parte decimal del nmero. Esto usando el peso de los bits. el bit decimal ms significativo vale 1/2, el siguiente vale 1/4, el otro 1/8, el otro 1/16 y as hasta completar lo que falta para los 23bits del valor. 5. Se concatena todo y ese es el valor flotante representado en memoria.

Caracteres
Los caracteres se representan utilizando el tipo char, que tiene slo 1 byte de tamao. Este tipo se utiliza para representar los 255 caracteres de la tabla de caracteres del sistema. El tipo char es tambin un tipo entero, ya que puede tomar valores de 0 a 255. En cuanto a la forma de declarar variables de tipo char es la misma forma que con los otros tipos. char a; char a = 's'; char a = 48; Como puedes ver, se le puede asignar un nmero a una variable char, ya que se trata de un tipo entero. En algunas situaciones particulares se utiliza el tipo char para contadores, porque permite que ocupen slo un byte en memoria. Es importante notar que con la llegada de la codificacin UTF-8, los caracteres de los diversos idiomas pueden ocupar 1, 2, 3 o 4 bytes, de modo que el tipo char ya no alcanza para la representacin de todos los caracteres. Por

Programacin en C/Tipos de datos ello, el estndar C99 introduce el tipo wchar que puede ocupar ms de 1 byte, segn sea necesario para la codificacin utilizada por el sistema.

11

Programacin en C/Interaccin con el usuario


En este captulo veremos un pco ms sobre como interactuar con el usuario de nuestros programas desde la consola, utilizando printf() como vimos en el primer ejemplo "Hola mundo", as como scanf() para la lectura del teclado.

Imprimir por pantalla


Como hemos visto hasta ahora en los ejemplos, hay una funcin que utilizamos para sacar por pantalla textos arbitrarios o el resultado de alguna operacin: la funcin printf(). Si miramos (en la documentacin) su definicin, no nos aclarar demasiado: int printf (const char *TEMPLATE, ...) ...claro que por algo tiene una seccin completa de la documentacin para ella sola. Veamosla poco a poco. Se trata de una funcin de la biblioteca estndar, lo que quiere decir que para utilizarla tenemos que incluir previamente su definicin. La encontraremos en <stdio.h>. Lo primero que vemos en la definicin es que es una funcin de tipo int, lo que quiere decir que devuelve un entero. Ese entero es el nmero de caracteres impresos en la pantalla, o un nmero negativo en caso de que se produzca algn error. Lo siguiente a notar es su primer argumento: const char *TEMPLATE. Se trata de una cadena de caracteres (char *) que no ser modificada por la funcin (const), con lo que puede ser una constante de cadena o una variable que contenga una cadena, pero siempre debe acabar con el carcter nulo \0. Y luego vienen esos extraos puntos suspensivos. Esa elipsis nos indica que como argumentos adicionales de printf() podemos poner una serie ilimitada de otros argumentos, que se supone que la funcin sabr qu hacer con ellos. Y eso es justamente lo que hace tan fabulosa y til a printf(). El uso ms simple de printf() es imprimir una cadena de texto simple y corriente. Como ya vimos: printf("Hola Mundo\n"); /*imprime la cadena*/ Y vimos que printf() tambin puede, con un argumento extra y una sintaxis especial, imprimir un nmero entero que hayamos almacenado en una variable: char resultado; resultado=5+2; printf("Resultado de la suma: %i\n",resultado); Aqu el punto de insercin es la secuencia %i. printf() siempre trata las secuencias que comiencen por % como secuencias de control que le dicen que debe imprimir algo que le proporcionamos en los otros argumentos. As, podemos imprimir varios enteros distintos en los sitios que queramos de la cadena, insertando varias de estas secuencias %i: int numero; numero=3; printf("El doble de %i es %i y su cuadrado es %i\n",numero,numero*2,numero*numero);

Programacin en C/Interaccin con el usuario

12

Lectura de datos del teclado


La entrada de datos se puede hacer de muchas maneras y entre ellas estn desde el uso de dispositivos especiales hasta nuestro simple teclado. La entrada de datos se refiere a cualquier forma de influencia del usuario sobre los datos que posee el sistema. Con el fin de mostrar una forma de entrada simple para el aprendizaje vamos a hablar del scanf() que se encuentra definido en el <stdio.h> y que se usa para capturar diferentes tipos de datos.

El scanf()
El scanf() es unas de las funciones ms usadas por los principiantes para hacer entrada de datos en el lenguaje C. Tiene una sintaxis muy parecida al printf. Se da una cadena con el formato de los datos y luego se ponen las variables en orden que correspondan a ese tipo de datos. O sea que como en el printf se pueden mostrar por pantalla los datos de varias variables en una misma sentencia, en el scanf se pueden capturar varios datos en una sola sentencia. #include <stdio.h> int main() { int a; printf ("diga un valor para a:"); scanf("%i",&a); printf ("el valor es: %i\n",a); return 0; } Por ahora no nos interesan las dems sentencias, slo el scanf. En el cdigo se ve lo siguiente: scanf("%i",&a); Se observa que la funcion printf dej en pantalla una peticin para que el usuario introdujera un valor. Entonces, el scanf recibe como argumento una cadena del formato en que se van a capturar los datos y la lista de variables que van a recibir valores y que deben coincidir con los del formato. En este caso slo se muestra a. El smbolo (&) que precede a la variable a es para especificar que lo que se est mandando como argumento no es el valor que posee la variable a sino la direccin en que se encuentra. En este momento eso no tiene mucha relevancia, slo hay que recordar que se debe usar el smbolo dentro del scanf. En el momento en que se hable de punteros se darn ms detalles de esto. Otro ejemplo del uso de scanf: #include <stdio.h> int main() { int a,b; printf ("introduzca dos valores con el formato \"a,b\" :"); scanf("%i,%i",&a,&b); printf ("el primer valor : %i\n",a); printf ("el segundo valor : %i\n",b); return 0; } Este otro ejemplo no necesita mucha explicacin, slo ha cambiado que se ha introducido una nueva variable en el cdigo y que sta es capturada por el mismo scanf(). Obviamente el orden de los argumentos es muy importante. El primer %i representa la variable a y el segundo representa la b. En el momento de introducir los datos es necesario

Programacin en C/Interaccin con el usuario poner la coma para separar los valores, si no el scanf, que es una funcin un poco quisquillosa, puede dar errores.

13

Programacin en C/Expresiones
Vamos a tratar ahora de que el ordenador haga un poco de matemticas para nosotros. Por ejemplo, que realice unas pocas sumas, restas multiplicaciones y divisiones. #include <stdio.h> int main(void) { int resultado; resultado=5+2; printf("Resultado resultado=5-2; printf("Resultado resultado=5*2; printf("Resultado resultado=5/2; printf("Resultado return(0); } Despus de grabarlo (por ejemplo, con el nombre ejemplo.c), lo compilamos y ejecutamos, con (respectivamente): $ gcc ejemplo.c $ ./a.out Resultado de la Resultado de la Resultado de la Resultado de la $

de la suma: %i\n",resultado); de la resta: %i\n",resultado); de la multiplicacin: %i\n",resultado); de la divisin: %i\n",resultado);

suma: 7 resta: 3 multiplicacin: 10 divisin: 2

Fijmonos en la lnea del principio de la funcin main: int resultado; Esta lnea lo que hace es reservar un trozo de memoria, del tamao de un int (4 bytes), y asignarle el nombre resultado, para poder despus referirnos a l. A partir de este momento, podemos considerar que en nuestro programa existe una variable, que no tiene valor definido, pero a la que le podremos dar valor posteriormente. Las lneas con printf() ya las conocemos, pero hay algo en ellas que no habamos visto antes. Esos %i y la parte de resultado son nuevas para nosotros. La funcin printf() no slo sabe imprimir cadenas simples, como "Hola Mundo\n" , sino tambin imprimir variables. Para ello, en el lugar de la cadena donde queremos que aparezca el valor de la variable, introducimos lo que se llama una cadena de conversin de printf(). Estas cadenas siempre empiezan por % , siendo %i la cadena para imprimir un entero, como es en nuestro caso int resultado . Finalmente, printf() debe saber qu valor escribir, por eso le damos otro argumento (u otros), usando , como separador, que contienen las variables cuyos valores queremos mostrar.

Programacin en C/Expresiones En el resto del programa hemos visto cmo decirle al ordenador que ejecute una suma, una resta, una multiplicacin y una divisin entera, con los operadores +, -, * y /. Es de notar que el resultado de una operacin como estas entre nmeros enteros ser siempre otro entero, como se puede observar en la divisin, en la que no obtenemos un bonito decimal, sino un resultado entero. Adems, hemos visto que el resultado de esas operaciones, que llamamos expresiones, puede ser asignado a una variable: resultado = 7; Esa asignacin se hace mediante el operador de asignacin: =. Con l, ya conocemos cinco operadores. Pero, como = tambin es un operador, cmo sabe el ordenador qu operador debe ejecutar primero? Y si es un operador, por qu no da un resultado? No crea una expresin?
Operadores */ += Precedencia Izq. a Der. Izq. a Der. Der. a Izq.

14

Empezando por las ltimas preguntas, el operador de asignacin s crea una expresin, como los operadores de suma, resta, multiplicacin y divisin, y esa expresin tiene un resultado, que es el valor que obtiene el lado izquierdo al realizar la operacin. En cuanto a saber qu se debe ejecutar primero, el ordenador tiene una lista de precedencia, segn la cual siempre ejecuta primero las multiplicaciones y divisiones, de izquierda a derecha, a continuacin las sumas y restas, de izquierda a derecha, y a continuacin las asignaciones, de derecha a izquierda. Para ms detalles acerca de la precedencia de los operadores ver el anexo de los operadores. En cuanto a los caracteres de punto y coma, notamos aqu que una expresin tambin puede ser una sentencia por s misma, sin necesidad de que haya ninguna funcin. De hecho, una sentencia puede no tener siquiera una expresin. La lnea: ; es una sentencia perfectamente vlida, la sentencia vaca, que sera til en puntos donde el lenguaje requiera una sentencia pero no sea necesaria para nuestro programa.

Programacin en C/Instrucciones de control

15

Programacin en C/Instrucciones de control


Como ya se ha mencionado, C es un ejemplo de programacin estructurada. En este tipo de programacin, es necesario contar con ciertas estructuras que permitan controlar el flujo del programa, es decir, tomar decisiones y repetir acciones.

La estructura condicional if ... else


En la gran mayora de los programas ser necesario tomar decisiones sobre qu acciones realizar. Esas decisiones pueden depender de los datos que introduzca el usuario, de si se ha producido algn error o de cualquier otra cosa. La estructura condicional if ... else es la que nos permite tomar ese tipo de decisiones. Traducida literalmente del ingls, se la podra llamar la estructura "si...si no", es decir, "si se cumple la condicin, haz esto, y si no, haz esto otro". Un ejemplo sencillo sera el siguiente (no se trata de un programa completo, sino tan slo una porcin de cdigo): if (edad sweeps < 18) printf("No puedes acceder.\n"); else printf("Bienvenido.\n"); Este cdigo de ejemplo dice que si edad es menor que 18 se imprimir "No puedes acceder.\n" , mientras que en caso contrario se imprimir "Bienvenido.\n" . Como se ve en el ejemplo, la estructura de un condicional es bastante simple: if (condicin) { sentencias_si_verdadero; } else { sentencias_si_falso; } Cuando la condicin sea verdadera, se ejecutarn las sentencias dentro del primer bloque de cdigo, cuando la condicin sea falsa, se ejecutarn las sentencias del segundo bloque de cdigo. La indentacin (los espacios al comienzo de las lneas) no es necesaria, pero ayuda a la claridad del cdigo. La utilizacin de las llaves { ...} es obligatoria cuando se quiere utilizar ms de una instruccin por bloque, y optativa cuando slo se quiere escribir una instruccin. Por claridad, sin embargo, es recomendable utilizarlas an cuando slo vaya a haber una instruccin. El bloque del else es opcional. Si no se lo encuentra, slo se realizar la accin correspondiente al bloque if . A continuacin, un ejemplo con una funcin, que devuelve el mayor de dos nmeros: int mayor(int a, int b) { int elmayor = a; if (b > a) { elmayor = b; } return elmayor; }

Programacin en C/Instrucciones de control

16

Operadores de Comparacin
Las condiciones son expresiones que utilizan operadores para tomar la decisin apropiada. Son los operadores de comparacin, y su resultado puede ser 1 (equivalente a verdadero) o 0 (equivalente a falso). Este resultado provocar que se ejecute el cdigo del bloque if o el del bloque else , segn corresponda. A continuacin un listado de los posibles operadores de comparacin en C y su significado.

Operadores de Comparacin
Operador Significado estrictamente menor que estrictamente mayor que menor o igual que mayor o igual que igual a distinto de

< > <= >= == !=

Por otro lado, en C se toma como falso el valor 0, y como verdadero cualquier otro valor. Esto nos permite utilizar condiciones sin operador: float division(int { if (divisor) return } else { printf return } } dividendo, int divisor) { dividendo / divisor; ("No se puede dividir por cero\n"); 0;

Agrupacin de operadores
Adems, es posible utilizar varios de estos operadores a la vez, mediante los operadores lgicos "y", "o" y "no"; los operadores correspondientes en C son && , || y ! , respectivamente. Para realizar combinaciones de operadores, se utilizan parntesis que agrupan las condiciones. Hay que notar que el operador ! es unario, es decir, no lleva ningn operando a su izquierda, afecta slo al operando que hay a su derecha. Un ejemplo donde se ven agrupaciones de operadores puede ser la decisin de si un ao es bisiesto o no. Los aos son bisiestos si son divisibles por 4, pero no si son divisibles por 100, a menos que tambin sean divisibles por 400. if ( (!(a % 4) && ((a % 100))) || !(a % 400) ) printf("es un ao bisiesto.\n"); } else { printf("no es un ao bisiesto.\n"); } {

En este caso, se utiliza la operacin mdulo, que obtiene el resto de la divisin entera de un nmero por otro. Cuando un nmero es divisible por otro, el resto de su divisin entera ser cero. Siendo que cero es equivalente a falso, y cualquier valor distinto de cero es equivalente a verdadero, podemos usar el operador ! para verificar si el nmero

Programacin en C/Instrucciones de control es mltiplo de 4, de 100 o de 400.

17

Evaluacin de corto circuito


La evaluacin en corto circuito es una caracterstica del lenguaje C que se utiliza para optimizar la ejecucin de programas. Consiste en que el programa puede verificar si una expresin es verdadera o falsa antes de haber evaluado toda condicin. Por ejemplo, si se tiene una condicin como la siguiente: if ((a > 2) || (b < 4)) { ... } Al ejecutarse el programa, se evaluar primero si a > 2 . En el caso en que sea verdadero, no continuar con la siguiente condicin, ya que el resultado ser de cualquier modo verdadero. De la misma forma, si la condicin fuera: if ((a > 2) && (b < 4)) { ... } En este caso, si no se cumple que a > 2 , no se evaluar la siguiente condicin, ya que el resultado ser falso de todos modos. Esta caracterstica no tiene demasiada importancia al comenzar a programar, pero facilitar ciertas operaciones y optimizaciones en programas avanzados.

La estructura condicional switch ... case


La estructura condicional switch ... case se utiliza cuando queremos evitarnos las llamadas escaleras de decisiones. La estructura if nos puede proporcionar, nicamente, dos resultados, uno para verdadero y otro para falso. Una estructura switch ... case , por su parte, nos permite elegir entre muchas opciones: ejemplo con un salario con la condicion switch /*si*/ #include <stdio.h> #include <stdlib.h> main() { int nivel; float salario, slanuevo; printf("introduce tu nivel\n"); scanf("%d",&nivel); printf("dame tu salario\n "); scanf("%f",&salario); switch (nivel) { case 1: slanuevo=salario+(salario*.035); printf("tu salario es:%f\n",slanuevo); break; case 2:

Programacin en C/Instrucciones de control slanuevo=salario+(salario*.041); printf("tu salario es:%f\n",slanuevo); break; case 3: slanuevo=salario+(salario*.048); printf("tu salario es:%f\n",slanuevo); break; case 4: slanuevo=salario+(salario*.053); printf("tu salario es:%f\n",slanuevo); break; default: printf("tu salario es:%f\n",salario); } system ("pause"); return 0; } Otro ejemplo: #include <stdio.h> #include <stdlib.h> int main(void) { int dia; printf("que numero de da de la semana es?"); scanf("%d",&dia); switch(dia) { case 1 : printf("Lun, Luna"); break; case 2 : printf("Mar, Marte"); break; case 3 : printf("Mier, Mercurio"); break; case 4 : printf("Jue, Jupiter"); break; case 5 : printf("Vie, Venus"); break; case 6 : printf("Sab, Saturno"); break;

18

Programacin en C/Instrucciones de control case 7 : printf("Dom, Sol"); break; default : printf("No existe"); } system ("pause"); return 0; } La estructura anterior, de realizarse con sentencias if , necesitara cuatro de ellas, resultando un enorme bloque muy difcil de leer. En la mayora de los casos, adems, la sentencia switch proporciona una ganancia en velocidad del cdigo, pues permite al compilador trabajar en base a que se trata de una decisin mltiple para una nica variable, cosa que con sentencias if el compilador no tiene por qu detectar. Como vemos, para cada valor de la variable se ejecuta un bloque de sentencias distinto, en el que no necesitamos llaves. Hay un caso especial, default , que se ejecuta si ningn otro corresponde, y que no es necesario poner. Es, en todo, equivalente al bloque else de una sentencia if . Las sentencias break son muy importantes, ya que el comportamiento normal de un bloque switch es ejecutarlo todo desde la etiqueta case que corresponda hasta el final. Por ello, si no queremos que se nos ejecute ms de un bloque, pondremos sentencias break al final de cada bloque excepto el ltimo. Es decir, las etiquetas case son puntos de entrada de la ejecucin, y no implican que al acabarse el bloque case la ejecucin salte al final del bloque switch . Las etiquetas case siguientes a la que hemos utilizado para entrar son, sencillamente, ignoradas. A la ausencia de sentencias break se le llama, en ocasiones, "dejar caer la cascada switch ".

19

El bucle while
El bucle while sirve para ejecutar cdigo reiteradas veces. while (/* Condicin de ejecucin del bucle */) { /* Cdigo */ } La condicin ha de ser una sentencia que devuelva un valor booleano, y esta puede ser el valor booleano en s, verdadero (true o 1) si la condicin se cumple, o falso si esta no se cumple (false o 0). Tambin puede ser el nombre de una variable, y el valor de la expresin depender de su contenido. Aunque sea una variable no booleana, siempre se podr usar, si vale 0 ser como si la condicin no se cumpliera, y siempre que sea diferente de 0, se considerar que la condicin se cumple. La forma ms obvia tal vez, y la ms usada sin duda, son las sentencias comparativas, que usan los operandos ==, !=, <=, >=, <, >. Su uso sera as: variable o valor inmediato operador variable o valor inmediato Tened en cuenta que adems de las variables y los valores inmediatos, tambin se pueden poner all llamadas a funciones que devuelvan un valor. int tecla = 0; while (tecla == 0) { tecla = readkey(); /* Intentamos leer una pulsacin de tecla */ }

Programacin en C/Instrucciones de control En este ejemplo lo que hacemos es que el programa se detenga hasta que el usuario pulse una tecla, con lo que cambiar el valor de la variable "tecla".

20

El bucle for
El bucle for es un bucle muy flexible y a la vez muy potente ya que tiene varias formas interesantes de implementarlo, su forma ms tradicional es la siguiente: for (/* inicializacin */; /* sentencia condicional */; /* incremento o decremento */) { /* cdigo a ejecutar */ } Inicializacin: en esta parte se inicia la variable que controla el bucle y es la primera sentencia que ejecuta el bucle, solo se ejecuta una vez ya que solo se necesita al principio del bucle. Sentencia condicional: es la segunda sentencia que ejecuta el bucle, es una sentencia condicional vlida, que determina si el bucle se ejecutar o no. incremento o decremento: es la ultima sentencia que ejecuta el bucle por lo general incrementar la variable con que se inicio el ciclo, despus de eso el bucle revisa nuevamente la condicin, si es verdadera tiene lugar una ejecucin ms del cuerpo del ciclo, si es falsa se termina el ciclo y as la repeticin. Aqu muestro un ejemplo de un bucle que se ejecuta 100 veces: for (int i=0; i < 100; i++) { printf("%i\n", i); } No es necesaria por ejemplo en la primera la sentencia iniciar la variable que utilizara el bucle si ya ha sido inicializada en otra parte del bloque de sentencias donde se encuentra el bucle. Por lo general la sentencia condicional se compone de la variable que se utiliz para la inicializacin del bucle a la cual se le aplica alguno de los operadodores relacionales <, <=, >, >= ; junto con otro valor; esta parte del bucle tambin se puede implementar con una variable de tipo bool. NOTA: recuerde que en cualquier sentencia donde se evala una expresin vlida, un valor de retorno=0 se considera falso y cualquier valor distinto de cero es verdadero, as false=0 y true=1. Creo que es importante comentar que esos tres "parmetros" que se le pasan a la sentencia for se pueden omitir, pero los punto-y-coma entre ellos s se han de dejar. Tambin cabe destacar que si no se pone la condicin de ejecucin del bucle, este se ejecutar indefinidamente hasta que se ejecute una sentencia break o se termine la funcin o el programa mediante un return.

El bucle do...while
El bucle do...while es un bucle que, por lo menos, se ejecuta una vez. Do significa literalmente "hacer", y while significa "mientras" Su forma es esta: do { /* CODIGO */ } while (/* Condicin de ejecucin del bucle */) Os muestro un ejemplo sencillo de uso: int aleatorio; do {

Programacin en C/Instrucciones de control aleatorio = rand(); } while (aleatorio != 25); La verdad es que este ejemplo puede resultar un poco absurdo, pero es bastante intuitivo. El cdigo del bucle asigna un valor aleatorio a la variable definida anteriormente, y mientras esa variable no tenga el valor 25, el bucle sigue ejecutndose.

21

El goto
La sentencia goto sirve para indicar al programa que continue ejecutndose desde la lnea de cdigo indicada. La verdad es que es una sentencia poco aceptada por la comunidad de programadores, pues puede provocar que se hagan programas un poco "sucios" y confusos. Su sintaxis es ms o menos as: /* Cdigo */ ETIQUETA: /* Cdigo */ goto ETIQUETA; /* Cdigo */ As, cuando se ejecute la sentencia goto , el programa continuar su ejecucin a partir de la etiqueta marcada. Como se puede observar se puede usar para crear un bucle, o para ir a una parte del cdigo u otra si se combina con una sentencia if...else . pero para los bucles ya hay estructuras definidas, y para la ejecucin opcional de bloques de cdigo, ya existen las Funciones. Slo en ocasiones muy excepcionales ser recomendado el uso del goto al crear iteraciones muy complejas. Sin embargo, con el pasar de los aos este comando ya ha quedado prcticamente descartado de el lenguaje de los programadores profesionales, debido a que ocasionaba muchas fallas en el momento de compilar.

Programacin en C/Uso de funciones

22

Programacin en C/Uso de funciones


Funciones
Como vimos anteriormente C tiene como bloque bsico la funcin. main() es una funcin, printf() otra, y hay muchas ms funciones predefinidas, pero nosotros mismos tambin podemos definir nuestras propias funciones. De hecho, es fundamental hacerlo. Podemos definir una funcin cualquiera de la misma manera en que definimos la funcin main() . Basta con poner su tipo, su nombre, sus argumentos entre parntesis y luego, entre llaves, su cdigo: /* Inclusin de archivos */ #include <stdio.h> void holamundo(void) /*Funcin donde se ejecuta la lgica del programa*/ { printf("Hola Mundo\n"); /*imprime la cadena*/ return; /*sale de la funcin*/ } int main(void) /*Funcin principal del programa*/ { holamundo(); /*llamada a la funcin que lleva el peso*/ return 0; /*sale del programa: correcto*/ } Este cdigo es en todo equivalente al "Hola Mundo" original, slo que nos muestra cmo escribir y cmo utilizar una funcin. Y adems nos muestra un principio de buena programacin: meter las sentencias que "hacen el trabajo" en otras funciones especficas para sacarlas de main() , dejando en sta tan slo un guin general de lo que hace el programa, no las rdenes especficas. De esta manera se facilita la comprensin del programa, y por tanto el futuro trabajo de modificarlo. De la misma manera que tenemos que declarar una variable antes de utilizarla, no es indiferente el orden en que se siten las diferentes funciones en el fichero: las funciones deben declararse antes de ser llamadas. Igualmente vemos que, para una funcin void , la sentencia de control return no puede llamarse como pseudofuncin, porque en dicho caso la funcin void (en nuestro caso holamundo() ) devolvera un valor, cosa que su definicin no permite. Las funciones tambin permiten recibir tipos de datos, as pues las funciones nos sirven para hacer de un gran problema pequeas partes de un problema o sea dividir un gran problema en diferentes problemas ms pequeos. As que las funciones tambin pueden retornar un tipo de dato que hemos definido dentro de la misma. La definicin de una funcin para sumar dos nmeros sera de la siguiente manera: /* * suma.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro "Programacin en C" * bajo licencia FDL, adaptado del Dominio Pblico

Programacin en C/Uso de funciones */ #include <stdio.h> int sumar(int numero1, int numero2); /* prototipo de la funcin */ int main(void) { int suma; /* definimos una variable*/ suma = sumar(5, 3); /* la variable obtiene el valor retornado de sumar * puede verse que entre los parntesis se mandan los valores * de los nmeros que se desean sumar en este caso 5 y 3 * pueden haberse declarados estos dos nmeros en otras variables * int dato = 4, valor = 3; * suma = sumar(dato, valor); */ /* imprimimos la suma de los dos nmeros */ printf("La suma es: %d ", suma); return 0; } int sumar(int numero1, int numero2) { int retsuma; /* creamos la variable a retornar*/ retsuma = numero1 + numero2; /* asignamos a esa variable la suma de nmero 1 y 2*/ return retsuma; /* retornamos la suma de los nmeros */ }

23

Paso de Parmetros
Las funciones pueden recibir datos como lo hemos observado, pero existen dos formas importantes de enviar los datos hacia una funcin por valor y por referencia, con las cuales observarse que son muy diferentes y que una puede ser muy diferente de la otra, haciendo modificaciones en nuestro programa. Por Valor El paso por valor enva una copia de los parmetros a la funcin por lo tanto los cambios que se hagan en ella no son tomados en cuenta dentro de la funcin main() . Ejemplo: /* * por_valor.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009

Programacin en C/Uso de funciones * * para el wikilibro "Programacin en C" * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> void sumar_valor(int numero); /* prototipo de la funcin */ int main(void) { int numero = 57; /* definimos numero con valor de 57*/ sumar_valor(numero); /* enviamos numero a la funcin */ printf("Valor de numero dentro de main() es: %d\n", numero); /* podemos notar que el valor de numero se modifica * slo dentro de la funcin sumar_valor pero en la principal * nmero sigue valiendo 57 */ return 0; } void sumar_valor(int numero) { numero++; /* le sumamos 1 al numero */ /* el valor de nmero recibido se aumenta en 1 * y se modifica dentro de la funcin sumar_valor() */ printf("Valor de numero dentro sumar_valor() es: %d\n", numero); return; } Por Referencia El paso por referencia se hace utilizando apuntadores. Se enva la direccin de memoria de la variable, por lo tanto los cambios que haga la funcin si afectan el valor de la variable. Ejemplo: /* * por_referencia.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro "Programacin en C" * bajo licencia FDL, adaptado del Dominio Pblico */

24

Programacin en C/Uso de funciones

25

#include <stdio.h> void sumar_referencia(int *numero); /* prototipo de la funcin */

int main(void) { int numero = 57; /* definimos numero con valor de 57*/ sumar_referencia(&numero); /* enviamos numero a la funcin */ printf("\nValor de numero dentro de main() es: %d ", numero); /* podemos notar que el valor de numero se modifica * y que ahora dentro de main() tambin se ha modificado * aunque la funcin no haya retornado ningn valor. */ return 0; } void sumar_referencia(int *numero) { *numero += 1; /* le sumamos 1 al numero */ /* el valor de numero recibido se aumenta en 1 * y se modifica dentro de la funcin */ printf("\nValor de numero dentro sumar_referencia() es: %d", *numero); return; }

Variables Locales y Globales


Adems de pasar valores a una funcin, tambin se pueden declarar tipos de datos dentro de las funciones, estos tipos de datos declarados dentro de una funcin solo son accesibles dentro de esta misma funcin y se les conocen como variables locales, as pues podemos definir los mismos nombres de variables en diferentes funciones, ya que estas variables solo son accesibles dentro de esas funciones. Ejemplo: /* * locales.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro "Programacin en C" * bajo licencia FDL, adaptado del Dominio Pblico */

Programacin en C/Uso de funciones

26

#include <stdio.h> void funcion1() { int dato = 53; /* definimos dato en 53*/ char num1 = 'a'; /* num1 vale a */ /* imprimimos */ printf("Funcion1, dato=%d, num1=%c\n", dato, num1); return; } void funcion2() { int dato = 25; /* definimos dato en 25*/ char num2 = 'z'; /* num2 vale z*/ /* imprimimos */ printf("Funcion2, dato=%d, num2=%c\n", dato, num2); return; } int main(void) { funcion1(); /* llamamos a funcion1() */ funcion2(); /* llamamos a funcion2() */ return 0; } En este caso la variable dato, esta definida dentro de cada una de las funciones y son totalmente distinta una de otra y no se puede utilizar fuera de esta, as pues num2 no puede ser utilizada por la funcion1() y num1 tampoco puede ser utilizada por funcion2(). Existen pues variables que se definen fuera de la funcin principal main() y fuera de cualquier otra funcin creada por nosotros, estas variables se les conoce con el nombre de Variables Globales ya que se pueden utilizar dentro de main() y dentro de cualquier funcin creada por nosotros. Ejemplo: /* * * * * * *

global.c Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 para el wikilibro "Programacin en C" bajo licencia FDL, adaptado del Dominio Pblico

Programacin en C/Uso de funciones */ #include <stdio.h> int variable_global = 99; /* inicializamos la variable global */ void funcion(); int main(void) { /* imprimimos el valor*/ printf("main(), acceso a variable_global %d\n", variable_global); /* llamamos a la funcin */ funcion(); return 0; } void funcion() { /* imprimimos el valor*/ printf("funcion(), acceso a variable_global %d\n", variable_global); return; }

27

Funciones Recursivas
La recursividad (recursin) es la propiedad por la cual una funcin se llama a s misma directa o indirectamente. La recursin indirecta implica utilizar ms de una funcin. Se puede considerar la recursividad como una alternativa a la iteracin. La recursin permite especificar soluciones naturales, sencillas, que seran, en caso contrario, difciles de resolver. Toda funcin recursiva debe contemplar un caso base o condicin de salida, para terminar, o la recursividad no podr terminar nunca. Una funcin recursiva podra definirse as: funcion_recursiva( /* parmetros recibidos por la funcin */ ) { /* Cdigo */ funcion_recursiva( ); /* llamada a la funcin misma */ /* Cdigo */ } Uno de los ejemplos ms representativos en la recursividad es el factorial de un numero ( n! ):

la definicin de recursividad del factorial es:

Programacin en C/Uso de funciones

28

En esta definicin, n = 0, es nuestro caso base, que le da fin a la recursividad. Entonces nuestro programa que calcula el factorial es: /* *factorial.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro "Programacin en C" * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> long factorial(int n) { if (n == 0) /* caso base */ return 1; /* como 0! = 1, se retorna 1*/ else return n * factorial (n - 1); /* llamada a esta misma funcin */ } int main(void) { /* en este caso se llama a la funcin y se imprime directamente*/ printf("%ld ", factorial(5)); return 0; } Tambin existen otros tipos de funciones recursivas como lo es el producto de dos nmeros. El producto de a donde a y b son nmeros enteros positivos seria: Solucin iterativa: b,

Solucin recursiva:

As pues

es:

Podemos ver que la multiplicacin de dos nmeros a, b se puede transformar en otro problema ms pequeo multiplicar a por (b-1), el caso base se produce cuando b = 0 y el producto es 0. Ejemplo:

Programacin en C/Uso de funciones /* * producto.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro "Programacin en C" * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> int producto(int a, int b) { if (b == 0) /* caso base */ return 0; /* como b = 0, se retorna 0*/ else return a + producto (a, b - 1); /* llamada a esta misma funcin */ } int main(void) { /* en este caso se llama a la funcin y se imprime directamente*/ printf("%i ", producto( 7, 3)); return 0; }

29

Recursividad indirecta o recursin mutua


Esta se produce cuando una funcin llama a otra, que esta a su vez terminar llamando de nuevo a la primera funcin. El siguiente programa visualiza el alfabeto utilizando recursin indirecta o mutua: /* * elalfabeto.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro "Programacin en C" * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> void funcionA(char c); /* se declara el prototipo de la funcin para que el llamado */ void funcionB(char c); /* a la misma en la funcin no sea implcita */ int main(void)

Programacin en C/Uso de funciones { funcionA('z'); /* llamado a funcionA */ return 0; } void funcionA(char c) { if (c > 'a') /* caso base mientras c no sea menor que A */ funcionB(c); /* llamado a la funcionB */ printf("%c ", c); /* imprimimos el valor de c */ *la variable es un parametro no utilizado para este proceso } void funcionB(char c) { funcionA(--c); /* llamado a la funcionA decrementando el valor de 'z' */ }

30

Recursin versus Iteracin


Tanto la iteracin como la recursin se basan en estructura de control: la iteracin utiliza una estructura repetitiva y la recursin una estructura de seleccin. La iteracin utiliza explcitamente una estructura repetitiva mientras que la recursin consigue la repeticin mediante llamadas repetitivas a funciones. La iteracin termina si la condicin del bucle no se cumple, mientras que la recursin termina cuando se reconoce un caso base. La recursin puede presentar desventajas ante la iteracin ya que se invoca repetidas veces al mecanismo de llamada de funciones y se necesita un tiempo mayor para realizar cada llamada. La razn por la cual se puede elegir u optar por usar recursividad es que existen muchos problemas complejos que poseen naturaleza recursiva y, en consecuencia, son mas fciles de implementar. Ejemplo Iterativo /* * iterativo.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro "Programacin en C" * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> long factorial(int numero);

Programacin en C/Uso de funciones

31

int main(int argc, char** argv) { int contador = 0; /* calcula el factorial de 0 a 10 */ for ( contador = 0; contador <= 10; contador++ ) printf("%d! = %ld\n", contador, factorial( contador )); return 0; } /* funcion factorial iterativa */ long factorial( int numero ) { long resultado = 1; int i = 0; /* declaracion de la funcin factorial iterativa */ for ( i = numero; i >= 1; i-- ) resultado *= i; return resultado; } Ejemplo Recursivo /* * recursivo.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro "Programacin en C" * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> long factorial(int numero); int main(int argc, char** argv) { int contador = 0; /* calcula el factorial de 0 a 10 */ for ( contador = 0; contador <= 10; contador++ ) printf("%d! = %ld\n", contador, factorial( contador ));

Programacin en C/Uso de funciones return 0; } /* funcin factorial recursiva */ long factorial( int numero ) { if ( numero <= 0 ) /* caso base */ return 1; /* casos bases: 0! = 1 y 1! = 1 */ else /* llamada recursiva */ return numero * factorial( numero - 1 ); /* llamada a la funcin factorial */ }

32

Programacin en C/Vectores
Los vectores son una forma de almacenar datos que permiten contener una serie de valores del mismo tipo, cada uno de los valores contenidos tiene una posicin asociada que se usar para accederlos. Est posicin o ndice ser siempre un nmero entero positivo. En C la cantidad de elementos que podr contener un vector es fijo, y en principio se define cuando se declara el vector. Los vectores se pueden declarar de la siguiente forma: tipo_elemento nombre[largo];

Esto declara la variable nombre como un vector de tipo_elementos que podr contener largo cantidad de elementos, y cada uno de estos elemento podr contener un valor de tipo tipo_elemento. Por ejemplo: double valores[128]; En este ejemplo declaramos un vector de 128 elementos del tipo double, los ndices de los elementos iran entre 0 (para el primer elemento y 127 para el ltimo). De la misma forma que con las otras declaraciones de variables que hemos visto se le puede asignar un valor iniciar a los elementos. O tambin se pueden declarar: tipo_elemento nombre[largo]={valor_0, valor_1, valor_2}; En caso estamos asignadole valores a los primeros 3 elementos del vector nombre. Notar que largo debe ser mayor o igual a la cantidad de valores que le estamos asignando al vector, en el caso de ser la misma cantidad no aporta informacin, por lo que el lenguaje nos permite escribir: tipo_elemento nombre[]={valor_0, valor_1, valor_2}; Que declarar nombre como el vector de largo 3. Para acceder a un elemento accederemos a travs de su posicin. Es decir: tipo_elemento elemento; ...

Programacin en C/Vectores elemento = nombre[2]; Asumiendo que tenemos el vector anterior definido estaramos guardando valor_2 en elemento. Veamos algunos ejemplos: /* * Ejemplo : El producto escalar de dos vectores */ #include <stdio.h> double producto_escalar(double v1[], double v2[], int d); int main() { const int largo = 3; double vector_1[] = {5,1,0}; double vector_2[] = {-1,5,3}; double resultado = producto_escalar(vector_1, vector_2, largo); // imprime el resultado printf("(%f, %f, %f) . (%f, %f, %f) = %f\n", vector_1[0], vector_1[1], vector_1[2], vector_2[0], vector_2[1], vector_2[2], resultado); return 0; } /* producto escalar entre dos vectores */ double producto_escalar(double v1[], double v2[], int d) { double resultado = 0; int i; for (i=0; i < d; i++) { resultado += v1[i] * v2[i]; } return resultado; } En el ejemplo anterior usamos los vectores de C para representar vectores matemticos y calcular el producto vectorial entre ellos. Una peculiaridad que se puede notar es que al recibir un arreglo en una funcin no se especifica el largo, volveremos a esto en un captulo posterior. Otra funcin clsica es la bsqueda de un mximo o mnimo, que podemos escribirla de la siguiente manera: int buscar_maximo(double valores[], int num_valores) { int maximo_pos = 0; for (int i = 1; i < num_valores; i++) { if (valores[i] > valores[maximo_pos]) {

33

Programacin en C/Vectores maximo_pos = i; } } return maximo_pos; } Otro ejemplo sencillo, calcular el promedio de los valores. double promedio(double valores[], int largo) { double suma=0; for (int i=0;i<largo;i++) { suma+=valores[i]; } return suma/largo; } Cuando una funcin recibe un vector por parmetro y cambia su contenido y el cambio es permanente (se ve an fuera de la funcin). Esto puede parecer extrao despus del nfasis que pusimos en resaltar que todos los parmetros de una funcin se reciben por valor, pero se aclarar en el siguiente capitulo. Mientras tanto usemos esto para definir una funcin que le aplique otra funcin que recibe por parmetro a cada elemento del vector, guardando el resultado en el mismo vector y una llamada de ejemplo a esta. void cuadrados(double vector[], int largo) { for (int i=0;i<largo;i++) { vector[i]=cuadrado(vector[i]); } } ... double cuadrado(double valor) { return valor*valor; } ... cuadrados(elementos,num_elem); ... De la misma forma que venimos usando vectores de tipos bsicos, podemos tener vectores de vectores, estos se declaran de la siguiente forma: int matriz[3][7]; int tabla[3][4]={ { 1, 2, 3, 4}, { 5, 6, 7, 8}, /* los espacios y saltos de lneas no son tomados en cuenta */ { 9,10,11,12} }; double v[2][2][2]; ... printf("tabla[0][1]: %i\n", tabla[0][3]); // Imprime 4 printf("tabla[2][0]: %i\n", tabla[2][0]); // Imprime 9 ...

34

Programacin en C/Vectores En este ejemplo tabla es un vector de longitud 3, cuyos elementos son vectores de longitud 4 de elementos de tipo int. En resumen, suponiendo que v[n] es un vector de cualquier tipo de dato con n cantidad de posiciones, al vector v se le aplican las siguientes reglas: 1. La primera posicin siempre ser v[0] 2. La ltima posicin es v[n-1] 3. En versiones previas a C99 n es una constante definida antes de la declaracin de v[n]

35

Programacin en C/Cadenas de caracteres


Las cadenas de caracteres (tambin llamadas cadenas o strings) son un tipo particular de vectores, son de hecho vectores de char, con la particularidad que tienen una marca de fin (el caracter '\0'), adems el lenguaje nos permite escribirlas como texto dentro de comillas dobles. Veamos unos ejemplos de su declaracin: char cadena_hola[]="Hola"; char otro_hola[]={'H','o','l','a','\0'}; // Igual al anterior char vector[]={'H','o','l','a'}; /* Un vector de 4 elementos, con los elementos 'H','o','l' y 'a' */ char espacio_cadena[1024]="Una cadena en C"; char cadena_vacia[]=""; Cmo vimos anteriormente al declarar un vector se define la cantidad de elementos que puede contener, en el caso de las cadenas se debe tener en cuenta el espacio adicional necesario para el \0. Viendo el ejemplo, tanto cadena_hola y otro_hola tienen un largo 5 y cadena_vacia tiene un largo de 1. Tambin vimos anteriormente que al usar vectores debemos tener en cuenta su largo, y as es que el largo o cantidad de elemento lo necesitamos en todas las funciones que definimos usando vectores y lo recibimos como un parmetro ms en estas, en el caso de las cadenas al tener una marca de fin podemos prescindir del largo y procesar una cadenas hasta llegar a la marca de fin. Por ejemplo, la siguiente funcin calcula el largo de una cadena: /* devuelve la cantidad de caracteres en cadena sin contar el '\0' */ int largo_cadena(char cadena[]) { int largo=0 while (cadena[largo]!='\0') largo++; return largo; } Se debe tener en cuenta que el largo de una cadena y el largo del vector con la que se representa son distintos, tanto por como largo_cadena() cuenta el largo de la cadena, como por espacio_cadena del ejemplo anterior. Algo bastante usual es necesitar unir dos cadenas, veamos un ejemplo: bool unir_cadenas(char destino[], char origen[], int largo) { int largo_origen = largo_cadena(origen); int largo_destino = largo_cadena(destino); if ( largo_origen+largo_destino+1 > largo ) { return false;

Programacin en C/Cadenas de caracteres } for (int i=0; i<largo_origen;i++) { destino[largo_destino+i] = origen[i]; } destino[largo_destino+largo_origen]='\0'; return true; } ... if ( unir_cadenas(espacio_cadena," que puede crecer hasta 1023 caracteres",1024) ) { ... Estos dos ejemplos son versiones simplificadas de funciones provistas por la biblioteca estndar de C a travs del encabezado string.h. Nuestro largo_cadena() es similar al strlen() de la biblioteca estndar, y unir_cadenas() se asemeja al strncat(). Si bien ver estas versiones nos sirven para entender las cadenas en C, en general ser preferible usar las funciones provistas por la biblioteca estndar, ya que podemos estar seguros que van a estar programadas de la mejor manera posible. Entre las funcione que provee la biblioteca estndar de C, las ms importantes son: largo = strlen(cadena) // Para obtener el largo de una cadena strcpy(destino, origen) // Copia el contenido de origen en destino // destino debe ser lo suficientemente grande strcat(destino, origen) // Agrega el contenido de origen al final de destino // destino debe ser lo suficientemente grander resultado = strcmp(cadena1, cadena2) // Compara dos cadenas // devuelve un valor menor, igual o mayor que 0 segn si cadena1 es menor, // igual o mayor que cadena2, respectivamente. posicion = strchr(cadena, caracter) // Devuelve la posicin en memoria de la primer // aparicin de caracter dentro de cadena posicion = strstr(cadena,subcadena) // Devuelve la posicin en memoria de la primer // aparicin de subcadena dentro de cadena Veamos algunos ejemplos usando <string.h> : #include <stdio.h> #include <string.h> ... char color[] = "rojo"; char grosor[] = "grueso"; ... char descripcion[1024]; strcpy(descripcion, "Lapiz color "); strncat(descripcion, color, 1024); strncat(descripcion, " de trazo ", 1024);

36

Programacin en C/Cadenas de caracteres strncat(descripcion, grosor, 1024); // descripcion contiene "Lapiz color rojo de trazo grueso" ... void intercambiar(char vector[], int pos1, int pos2); void invierte_cadena(char cadena[]) { int largo = strlen(cadena); for (int i=0; i < (largo/2); i++) { intercambiar(cadena, i, (largo-1)-i); } } void intercambiar(char vector[], int pos1, int pos2) { char aux=vector[pos1]; vector[pos1]=vector[pos2]; vector[pos2]=aux; }

37

Programacin en C/Manejo de archivos


As como hemos revisado la salida y entrada por pantalla y teclado respectivamente, veremos ahora la entrada y/o salida de datos utilizando ficheros, lo cual ser imprescindible para un gran nmero de aplicaciones que deseemos desarrollar.

Ficheros
El estndar de C contiene funciones varias para la edicin de ficheros, estas estn definidas en la cabecera stdio.h y por lo general empiezan con la letra f, haciendo referencia a file. Adicionalmente se agrega un tipo FILE, el cual se usar como apuntador a la informacin del fichero. La secuencia que usaremos para realizar operaciones ser la siguiente: Crear un apuntador del tipo FILE * Abrir el archivo utilizando la funcin fopen y asignndole el resultado de la llamada a nuestro apuntador. Hacer las diversas operaciones (lectura, escritura, etc). Cerrar el archivo utilizando la funcin fclose.

fopen
Esta funcin sirve para abrir y crear ficheros en disco. El prototipo correspondiente de fopen es: FILE * fopen (const char *filename, const char *opentype); Los parmetros de entrada de fopen son: filename: una cadena que contiene un nombre de fichero vlido. opentype: especifica el tipo de fichero que se abrir o se crear. Una lista de parmetros opentype para la funcin fopen son:

Programacin en C/Manejo de archivos "r" : abrir un archivo para lectura, el fichero debe existir. "w" : abrir un archivo para escritura, se crea si no existe o se sobreescribe si existe. "a" : abrir un archivo para escritura al final del contenido, si no existe se crea. "r+" : abrir un archivo para lectura y escritura, el fichero debe existir. "w+" : crear un archivo para lectura y escritura, se crea si no existe o se sobreescribe si existe. "a+" : abrir/crear un archivo para lectura y escritura al final del contenido

38

Adicionalmente hay tipos utilizando "b" (binary) los cuales no sern mostrados por ahora y que solo se usan en los sistemas operativos que no pertenecen a la familia de unix.

fclose
Esta funcin sirve para poder cerrar un fichero que se ha abierto. El prototipo correspondiente de fclose es: int fclose (FILE *stream); Un valor de retorno cero indica que el fichero ha sido correctamente cerrado, si ha habido algn error, el valor de retorno es la constante EOF. Un ejemplo pequeo para abrir y cerrar el archivo llamado fichero.in en modo lectura: #include <stdio.h> int main(int argc, char** argv) { FILE *fp; fp = fopen ( "fichero.in", "r" ); fclose ( fp ); return 0; } Como vemos, en el ejemplo se utiliz el opentype "r", que es para la lectura. Otra cosa importante es que el lenguaje C no tiene dentro de si una estructura para el manejo de excepciones o de errores, por eso es necesario comprobar que el archivo fue abierto con xito "if (archivo == NULL)". Si fopen pudo abrir el archivo con xito devuelve la referencia al archivo (FILE *), de lo contrario devuelve NULL y en este caso se debera revisar la direccion del archivo o los permisos del mismo. En estos ejemplos solo vamos a dar una salida con un retorno de 1 que sirve para sealar que el programa termino por un error.

feof
Esta funcin sirve para determinar si el cursor dentro del archivo encontr el final (end of file). Existe otra forma de verificar el final del archivo que es comparar el caracter que trae fgetc del archivo con el macro EOF declarado dentro de stdio.h, pero este mtodo no ofrece la misma seguridad (en especial al tratar con los archivos "binarios"). La funcin feof siempre devolver cero (Falso) si no es encontrado EOF en el archivo, de lo contrario regresar un valor distinto de cero (Verdadero). El prototipo correspondiente de feof es: int feof(FILE *fichero);

Programacin en C/Manejo de archivos

39

rewind
Literalmente significa "rebobinar", sita el cursor de lectura/escritura al principio del archivo. El prototipo correspondiente de rewind es: void rewind(FILE *fichero);

Lectura
Un archivo generalmente debe verse como un string (una cadena de caracteres) que esta guardado en el disco duro. Para trabajar con los archivos existen diferentes formas y diferentes funciones. Las funciones que podramos usar para leer un archivo son: char fgetc(FILE *archivo) char *fgets(char *buffer, int tamano, FILE *archivo) size_t fread(void *puntero, size_t tamano, size_t cantidad, FILE *archivo); int fscanf(FILE *fichero, const char *formato, argumento, ...);

Las primeras dos de estas funciones son muy parecidas entre si. Pero la tercera, por el numero y el tipo de parmetros, nos podemos dar cuenta de que es muy diferente, por eso la trataremos aparte junto al fwrite que es su contraparte para escritura.

fgetc
Esta funcin lee un caracter a la vez del archivo que esta siendo sealado con el puntero *archivo. En caso de que la lectura sea exitosa devuelve el caracter ledo y en caso de que no lo sea o de encontrar el final del archivo devuelve EOF. El prototipo correspondiente de fgetc es: char fgetc(FILE *archivo); Esta funcin se usa generalmente para recorrer archivos de texto. A manera de ejemplo vamos a suponer que tenemos un archivo de texto llamado "prueba.txt" en el mismo directorio en que se encuentra el fuente de nuestro programa. Un pequeo programa que lea ese archivo ser: #include <stdio.h> #include <stdlib.h> int main() { FILE *archivo; char caracter; archivo = fopen("prueba.txt","r"); if (archivo == NULL){ printf("\nError de apertura del archivo. \n\n"); }else{

printf("\nEl contenido del archivo de prueba es \n\n");

Programacin en C/Manejo de archivos while (feof(archivo) == 0) { caracter = fgetc(archivo); printf("%c",caracter); } } return 0; }

40

fgets
Esta funcin est diseada para leer cadenas de caracteres. Leer hasta n-1 caracteres o hasta que lea un retorno de lnea. En este ltimo caso, el carcter de retorno de lnea tambin es ledo. El prototipo correspondiente de fgets es: char *fgets(char *buffer, int tamao, FILE *archivo); El primer parmetro buffer lo hemos llamado as porque es un puntero a un espacio de memoria del tipo char (podramos usar un arreglo de char). El segundo parmetro es tamao que es el limite en cantidad de caracteres a leer para la funcion fgets. Y por ultimo el puntero del archivo por supuesto que es la forma en que fgets sabra a que archivo debe leer. #include <stdio.h> #include <stdlib.h> int main() { FILE *archivo; char caracteres[100]; archivo = fopen("prueba.txt","r"); if (archivo == NULL) exit(1); printf("\nEl contenido del archivo de prueba es \n\n"); while (feof(archivo) == 0) { fgets(caracteres,100,archivo); printf("%s",caracteres); } return 0; } Este es el mismo ejemplo de antes con la diferencia de que este hace uso de fgets en lugar de fgetc. La funcin fgets se comporta de la siguiente manera, leer del archivo apuntado por archivo los caracteres que encuentre y a ponerlos en buffer hasta que lea un caracter menos que la cantidad de caracteres especificada en tamao o hasta que encuentre

Programacin en C/Manejo de archivos el final de una linea (\n) o hasta que encuentre el final del archivo (EOF). En este ejemplo no vamos a profundizar mas que para decir que caracteres es un buffer, los pormenores seran explicados en la seccin de manejo dinmico de memoria. El beneficio de esta funcin es que se puede obtener una linea completa a la vez. Y resulta muy til para algunos fines como la construccin de un parser de algn tipo de archivo de texto.

41

fread
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream ); Esta funcin lee un bloque de una "stream" de datos. Efecta la lectura de un arreglo de elementos "count", cada uno de los cuales tiene un tamao definido por "size". Luego los guarda en el bloque de memoria especificado por "ptr". El indicador de posicin de la cadena de caracteres avanza hasta leer la totalidad de bytes. Si esto es exitoso la cantidad de bytes ledos es (size*count). PARAMETROS: ptr : Puntero a un bloque de memoria con un tamao mnimo de (size*count) bytes. size : Tamao en bytes de cada elemento (de los que voy a leer). count : Nmero de elementos, los cuales tienen un tamao "size". stream: Puntero a objetos FILE, que especifica la cadena de entrada.

fscanf
La funcin fscanf funciona igual que scanf en cuanto a parmetros, pero la entrada se toma de un fichero en lugar del teclado. El prototipo correspondiente de fscanf es: int fscanf(FILE *fichero, const char *formato, argumento, ...); Podemos ver un ejemplo de su uso, abrimos el documento "fichero.txt" en modo lectura y leyendo dentro de el. #include <stdio.h>

int main ( int argc, char **argv ) { FILE *fp; char buffer[100]; fp = fopen ( "fichero.txt", "r" ); fscanf(fp, "%s" ,buffer); printf("%s",buffer); fclose ( fp ); return 0; }

Programacin en C/Manejo de archivos

42

Escritura
As como podemos leer datos desde un fichero, tambin se pueden crear y escribir ficheros con la informacin que deseamos almacenar, Para trabajar con los archivos existen diferentes formas y diferentes funciones. Las funciones que podramos usar para escribir dentro de un archivo son: int fputc(int caracter, FILE *archivo) int fputs(const char *buffer, FILE *archivo) size_t fwrite(void *puntero, size_t tamano, size_t cantidad, FILE *archivo); int fprintf(FILE *archivo, const char *formato, argumento, ...);

fputc
Esta funcin escribe un carcter a la vez del archivo que esta siendo sealado con el puntero *archivo. El valor de retorno es el carcter escrito, si la operacin fue completada con xito, en caso contrario ser EOF. El prototipo correspondiente de fputc es: int fputc(int carcter, FILE *archivo); Mostramos un ejemplo del uso de fputc en un "fichero.txt", se escribira dentro del fichero hasta que presionemos la tecla enter. #include <stdio.h> int main ( int argc, char **argv ) { FILE *fp; char caracter; fp = fopen ( "fichero.txt", "r+" ); printf("\nIntrouce un texto al fichero: "); while((caracter = getchar()) != '\n') { printf("%c", fputc(caracter, fp)); } fclose ( fp ); return 0; }

Programacin en C/Manejo de archivos

43

fputs
La funcin fputs escribe una cadena en un fichero. No se aade el carcter de retorno de lnea ni el carcter nulo final. El valor de retorno es un nmero no negativo o EOF en caso de error. Los parmetros de entrada son la cadena a escribir y un puntero a la estructura FILE del fichero donde se realizar la escritura. El prototipo correspondiente de fputs es: int fputs(const char *buffer, FILE *archivo) para ver su funcionamiento mostramos el siguiente ejemplo: #include <stdio.h> int main ( int argc, char **argv ) { FILE *fp; char cadena[] = "Mostrando el uso de fputs en un fichero.\n"; fp = fopen ( "fichero.txt", "r+" ); fputs( cadena, fp ); fclose ( fp ); return 0; }

fwrite
Esta funcin est pensada para trabajar con registros de longitud constante y forma pareja con fread. Es capaz de escribir hacia un fichero uno o varios registros de la misma longitud almacenados a partir de una direccin de memoria determinada. El valor de retorno es el nmero de registros escritos, no el nmero de bytes. Los parmetros son: un puntero a la zona de memoria de donde se obtendrn los datos a escribir, el tamao de cada registro, el nmero de registros a escribir y un puntero a la estructura FILE del fichero al que se har la escritura. El prototipo correspondiente de fwrite es: size_t fwrite(void *puntero, size_t tamano, size_t cantidad, FILE *archivo); Un ejemplo concreto del uso de fwrite con su contraparte fread y usando funciones es: /* * * * * */

FicheroCompleto.c Copyright 2009 Julio Csar Brizuela <brizuela@linux-qxlk>

#include <stdio.h>

Programacin en C/Manejo de archivos void void void void menu(); CrearFichero(FILE *Fichero); InsertarDatos(FILE *Fichero); VerDatos(FILE *Fichero);

44

struct sRegistro { char Nombre[25]; int Edad; float Sueldo; } registro; int main(int argc, char** argv) { int opcion; int exit = 0; FILE *fichero; while (!exit) { menu(); printf("\nOpcion: "); scanf("%d", &opcion); switch(opcion) { case 1: CrearFichero(fichero); break; case 2: InsertarDatos(fichero); break; case 3: VerDatos(fichero); break; case 4: exit = 1; break; default: printf("\nopcion no valida"); } } return 0; } void menu() {

Programacin en C/Manejo de archivos printf("\nMenu:"); printf("\n\t1. Crear fichero"); printf("\n\t2. Insertar datos"); printf("\n\t3. Ver datos"); printf("\n\t4. Salir"); } void CrearFichero(FILE *Fichero) { Fichero = fopen("fichero", "r"); if(!Fichero) { Fichero = fopen("fichero", "w"); printf("\nArchivo creado!"); } else { printf("\nEl fichero ya existe!"); } fclose (Fichero); return; } void InsertarDatos(FILE *Fichero) { Fichero = fopen("fichero", "a+"); if(Fichero == NULL) { printf("\nFichero no existe! \nPor favor creelo"); return; } printf("\nDigita el nombre: "); scanf("%s", registro.Nombre); printf("\nDigita la edad: "); scanf("%d", &registro.Edad); printf("\nDigita el sueldo: "); scanf("%f", &registro.Sueldo); fwrite(&registro, sizeof(struct sRegistro), 1, Fichero);

45

Programacin en C/Manejo de archivos fclose(Fichero); return; } void VerDatos(FILE *Fichero) { int numero = 1; Fichero = fopen("fichero", "r"); if(Fichero == NULL) { printf("\nFichero no existe! \nPor favor creelo"); return; } fread(&registro, sizeof(struct sRegistro), 1, Fichero); printf("\nNumero \tNombre \tEdad \tSueldo"); while(!feof(Fichero)) { printf("\n%d \t%s \t%d \t%.2f", numero, registro.Nombre, registro.Edad, registro.Sueldo); fread(&registro, sizeof(struct sRegistro), 1, Fichero); numero++; } fclose(Fichero); return; }

46

fprintf
La funcin fprintf funciona igual que printf en cuanto a parmetros, pero la salida se dirige a un archivo en lugar de a la pantalla. El prototipo correspondiente de fprintf es: int fprintf(FILE *archivo, const char *formato, argumento, ...); Podemos ver un ejemplo de su uso, abrimos el documento "fichero.txt" en modo lectura/escritura y escribimos dentro de el. #include <stdio.h> int main ( int argc, char **argv ) {

Programacin en C/Manejo de archivos FILE *fp; char buffer[100] = "Esto es un texto dentro del fichero."; fp = fopen ( "fichero.txt", "r+" ); fprintf(fp, buffer); fprintf(fp, "%s", "\nEsto es otro texto dentro del fichero."); fclose ( fp ); return 0; }

47

Programacin en C/Estructuras y Uniones


En la creacion de soluciones para algunos problemas surge la necesidad de agrupar datos de diferente tipo o de manejar datos que serian muy dificil de describir en los tipos de datos primitivos, esta es la situacion en la que debemos aprovecharnos de las caracteristicas que hacen al lenguaje C especial, o sea el uso de estructuras uniones y punteros.

Estructuras
Una estructura contiene varios datos. La forma de definir una estructura es haciendo uso de la palabra clave struct. Aqui hay ejemplo de la declaracion de una estructura: struct mystruct { int int_member; double double_member; char string_member[25]; } variable; "variable" es una instancia de "mystruct" y no es necesario ponerla aqu. Se podria omitir de la declaracion de "mystruct" y ms tarde declararla usando: struct mystruct variable; Tambin es una prctica muy comn asignarle un alias o sinnimo al nombre de la estructura, para evitar el tener que poner "struct mystruct" cada vez. C nos permite la posibilidad de hacer esto usando la palabra clave typedef, lo que crea un alias a un tipo: typedef struct { ... } Mystruct; La estructura misma no tiene nombre (por la ausencia de nombre en la primera linea), pero tiene de alias "Mystruct". Entonces se puede usar as:

Programacin en C/Estructuras y Uniones Mystruct variable;

48

Note que es una convencion, y una buena costumbre usar mayscula en la primera letra de un sinnimo de tipo. De todos modo lo importante es darle algn identificador para poder hacer referencia a la estructura: podriamos tener una estructura de datos recursiva de algn tipo. Ejemplo de una estructura : /* * * * * * * * * * * * * */

estructura.c Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 para el wikilibro "Programacin en C (fundamentos)" bajo licencia FDL, adaptado del Dominio Pblico Nombre Miembro Titulo Artista Precio Total Canciones Tipo char[30] char[25] float int

#include <stdio.h> #include <string.h> /* definimos una estructura para cds */ struct cd { char titulo[30]; char artista[25]; float precio; int canciones; } Cd1 = { /* inicializamos la estructura Cd1 creaa con sus valores * usando las definiciones iniciales*/ "Canciones Bebe", /* titulo */ "Pinocho", /* artista */ 12.50, /* precio */ 16 /* total canciones */ }; int main(void) { struct cd Cd2; /* definimos una nueva estructura llamado cd2 */ /* asignamos valores a los tipos de datos del cd2 */ strcpy(Cd2.titulo, "New Age");

Programacin en C/Estructuras y Uniones /* la forma de insertar valores a un * tipo char en una estructura es usando strcpy * de la libreria string.h */ strcpy(Cd2.artista, "Old Man"); Cd2.precio = 15.00; Cd2.canciones = 12; /* la forma de acceder a los valores de una estructura */ /* es usando el "." despues de la definicion del dato*/ printf("\n Cd 1"); printf("\n Titulo: %s ", Cd1.titulo); printf("\n Artista: %s ", Cd1.artista); printf("\n Total Canciones: %d ", Cd1.canciones); printf("\n Precio Cd: %f ", Cd1.precio); printf("\n"); printf("\n Cd 2"); printf("\n Titulo: %s ", Cd2.titulo); printf("\n Artista: %s ", Cd2.artista); printf("\n Total Canciones: %d ", Cd2.canciones); printf("\n Precio Cd: %.2f ", Cd2.precio); /* el .2 que esta entre %f * sirve para mostrar unicamente * 2 decimales despues del punto*/ return 0; }

49

Estructuras Anidadas
Una estructura puede estar dentro de otra estructura a esto se le conoce como anidamiento o estructuras anidadas. Ya que se trabajan con datos en estructuras si definimos un tipo de dato en una estructura y necesitamos definir ese dato dentro de otra estructura solamente se llama el dato de la estructura anterior. Definamos una estructura en nuestro programa: struct empleado /* creamos una estructura llamado empleado*/ { char nombre_empleado[25]; char direccion[25]; char ciudad[20]; char provincia[20]; long int codigo_postal; double salario; }; /* las estructuras necesitan punto y coma (;) al final */ Y luego necesitamos una nueva estructura en nuestro programa:

Programacin en C/Estructuras y Uniones struct cliente /* creamos una estructura llamada cliente */ { char nombre_cliente[25]; char direccion[25]; char ciudad[20]; char provincia[20]; long int codigo_postal; double saldo; }; /* las estructuras necesitan punto y coma (;) al final */ Podemos ver que tenemos datos muy similares en nuestras estructuras, asi que podemos crear una sola estructura llamada infopersona con estos datos idnticos: struct infopersona /* creamos la estructura que contiene datos parecidos */ { char direccion[25]; char ciudad[20]; char provincia[20]; long int codigo_postal; }; /* las estructuras necesitan punto y coma (;) al final */ Y crear las nuevas estructuras anteriores, anidando la estructura necesaria: struct empleado /* se crea nuevamente la estructura */ { char nombre_empleado[25]; /* creamos direcc_empleado con "struct" del tipo "estructura infopersona" */ struct infopersona direcc_empleado; double salario; }; /* las estructuras necesitan punto y coma (;) al final */ <source lang=c> struct cliente /* se crea nuevamente la estructura */ { char nombre_cliente[25]; /* creamos direcc_cliente con "struct" del tipo "estructura infopersona" */ struct infopersona direcc_cliente; double saldo; }; /* las estructuras necesitan punto y coma (;) al final */ Y ac el ejemplo completo con estructuras anidadas: /* * * * *

50

estructura2.c Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009

Programacin en C/Estructuras y Uniones * * * * * * * * * */ para el wikilibro "Programacin en C (fundamentos)" bajo licencia FDL, adaptado del Dominio Pblico Nombre Miembro Titulo Artista Precio Total Canciones Tipo char[30] char[25] float int

51

#include <stdio.h> #include <string.h> /* creamos nuestra estructura con datos similares */ struct infopersona { char direccion[25]; char ciudad[20]; char provincia[20]; long int codigo_postal; }; /* las estructuras necesitan punto y coma (;) al final */ /* creamos nuestra estructura empleado */ struct empleado { char nombre_empleado[25]; /* agregamos la estructura infopersona * con nombre direcc_empleado */ struct infopersona direcc_empleado; double salario; }; /* las estructuras necesitan punto y coma (;) al final */ /* creamos nuestra estructura cliente */ struct cliente { char nombre_cliente[25]; /* agregamos la estructura infopersona * con nombre direcc_cliente */ struct infopersona direcc_cliente; double saldo; }; /* las estructuras necesitan punto y coma (;) al final */ int main(void) {

Programacin en C/Estructuras y Uniones /* creamos un nuevo cliente struct cliente MiCliente; */

52

/*inicializamos un par de datos de Micliente */ strcpy(MiCliente.nombre_cliente,"Jose Antonio"); strcpy(MiCliente.direcc_cliente.direccion, "Altos del Cielo"); /* notese que se agrega direcc_cliente haciendo referencia * a la estructura infopersona por el dato direccion */ /* imprimimos los datos */ printf("\n Cliente: "); printf("\n Nombre: %s", MiCliente.nombre_cliente); /* notese la forma de hacer referencia al dato */ printf("\n Direccion: %s", MiCliente.direcc_cliente.direccion); /* creamos un nuevo empleado struct empleado MiEmpleado; */

/*inicializamos un par de datos de MiEmplado */ strcpy(MiEmpleado.nombre_empleado,"Miguel Angel"); strcpy(MiEmpleado.direcc_empleado.ciudad,"Madrid"); /* para hacer referencia a ciudad de la estructura infopersona * utilizamos direcc_empleado que es una estructura anidada */ /* imprimimos los datos */ printf("\n"); printf("\n Empleado: "); printf("\n Nombre: %s", MiEmpleado.nombre_empleado); /* notese la forma de hacer referencia al dato */ printf("\n Ciudad: %s", MiEmpleado.direcc_empleado.ciudad); return 0; }

Uniones
La definicion de "union" es similar a la de "estructura", La diferencia entre las dos es que en una estructura, los miembros ocupan diferentes areas de la memoria, pero en una union, los miembros ocupan la misma area de memoria. Entonces como ejemplo: union { int i; double d; } u; El programador puede acceder a travs de "u.i" o de "u.d", pero no de ambos al mismo tiempo. Como "u.i" y "u.d" ocupan la misma rea de memoria, modificar uno modifica el valor del otro, algunas veces de maneras

Programacin en C/Estructuras y Uniones impredecibles. El tamao de una union es el de su miembro de mayor tamao. Ejemplo de una unin: /* * * * * * * */

53

uniones.c Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 para el wikilibro "Programacin en C (fundamentos)" bajo licencia FDL, adaptado del Dominio Pblico

#include <stdio.h> #include <string.h> /*Creamos una union*/ union frases { char mensajes[50]; char ayudas[50]; char lineas[50]; } palabra; /*Creamos una estructura*/ struct comparte { char mensajes[50]; char ayudas[50]; char lineas[50]; }Sistema; /*Notece que la estructura y la union tienen los mismos tipos de datos*/ int main(int argc, char** argv) { /*Inicializamos*/ strcpy(palabra.mensajes, "Primer Mensaje"); /*Inicializamos*/ strcpy(palabra.ayudas, "Una Ayuda"); printf("\nFrases en Union: "); /*Imprimimos mensajes de union*/ printf("\n1- %s", palabra.mensajes);

Programacin en C/Estructuras y Uniones /*Imprimimos ayudas de union*/ printf("\n2- %s", palabra.ayudas); /*Inicializamos*/ strcpy(Sistema.mensajes, "Primer Mensaje"); /*Inicializamos*/ strcpy(Sistema.ayudas, "Una Ayuda"); /* Podemos notar que aunque inicializamos los valores * al imprimir se tiene el mismo valor para cada miembro * de la estructura, esto se debe a que las uniones usan el * mismo espacio de memoria para todos los elementos * de la union, siendo del tamao de su miembro de * mayor tamao, en este caso 50 bytes. * Entonces los tres miembros creados dentro de la * union comparten esos 50 bytes. * Entonces el ultimo valor agregado a la union es * el que se tiene. */ printf("\n\nFrases en Struct: "); /*Imprimimos mensajes de struct*/ printf("\n1- %s", Sistema.mensajes); /*Imprimimos ayudas de union*/ printf("\n2- %s", Sistema.ayudas); /* En la estructura comparte, se reservan 150 bytes * de memoria para los tres miembros, en este caso * cada uno es independiente en memoria, asi pues se * puede inicializar cada uno o usar como un campo * independiente. */ return 0; }

54

Programacin en C/Estructuras y Uniones

55

Enumeraciones
Una enumeracion (enum) es un tipo definido con constante de tipo entero. En la declaracion de un tipo enum creamos una lista de tipo de datos que se asocian con las constantes enteras 0, 1, 2, 3, 4, 5... su forma de definirlas es la siguiente: enum { enumerador1, enumerador2, enumeradorn }; enum Nombre { enumerador1, enumerador2, enumeradorn }; En este caso al ser declaradas enumerador1 toma el valor entero de 0, enumerador2 el valor de 1 y asi sucesivamente para cada una de las expresiones siguientes. Al declarar la enum se puede asociar a los tipos de datos a valores constantes en vez de la asociacion que por defecto se realiza (0, 1, 2, ), se utiliza entonces este formato: enum Nombre { enumerador1 = valor_constante1, enumerador2 = valor_constante2, ... enumeradorn = valor_constanten, }; Un ejemplo de una enum: <source lang=c> enum Boolean { FALSE, TRUE }; Se definen dos constantes para las constantes true y false con valores iguales a 0 para False y 1 para True. Ejemplo: /* * * * * * * */

Enum.c Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 para el wikilibro "Programacin en C (fundamentos)" bajo licencia FDL, adaptado del Dominio Pblico

Programacin en C/Estructuras y Uniones #include <stdio.h> enum Boolean { FALSE, TRUE }; /* Se define un enum para emular las constantes * True y False con valores de 0 y 1. * Notese que las enum no necesitan ; al final * de cada tipo de dato. */ /* Definimos una funcion del tipo enum llamada numero*/ enum Boolean numero(char c); int main(int argc, char** argv) { char caracter; int Numeros = 0; printf("\nIntroduce un texto. Para terminar: Enter. \n\t"); /* Tenemos un while que mientras no se presione Enter * seguira leyendo un tipo de dato caracter */ while((caracter = getchar()) != '\n') { if (numero(caracter)) { Numeros++; } } printf("\nTotal de Numeros leidos: %d", Numeros); return 0; } enum Boolean numero(char c) { switch(c) { case '0': case '1': case '2': case '3': case '4': case '5':

56

Programacin en C/Estructuras y Uniones case case case case '6': '7': '8': '9': return TRUE; /* Mientras el caracter valga de 0 a 9 retornara TRUE (1) */ default: return FALSE; /* Por default retornara FALSE (0) */ } } En la siguiente enum se declaran las variables inicializando la primera y las demas con los siguientes valores enteros: /* * * * * * * */

57

Enum2.c Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 para el wikilibro "Programacin en C (fundamentos)" bajo licencia FDL, adaptado del Dominio Pblico

#include <stdio.h> enum DiasSemanas { Domingo = 2, Lunes, Marte, Miercoles, Jueves, Viernes, Sabado }; /* Podemos inicializar nuestra primer constante Domingo * en 2, asi pues las demas los siguientes valores enteros. */ int main(int argc, char** argv) { enum DiasSemanas dia; for (dia = Domingo; dia <= Sabado; dia++) { printf("%d ", dia); /* Salida: 2 3 4 5 6 7 8 */ }

Programacin en C/Estructuras y Uniones

58

return 0; } A los enumeradores se pueden asignar valores o expresiones constantes durante la declaracion: enum Hexaedro { VERTICE = 8, LADOS = 12, CARAS = 6 };

Programacin en C/Punteros
Un puntero es una variable que contiene la direccin, o ubicacin en memoria, de algn valor.

Declarando puntero
Tenga en cuenta el siguiente bloque de cdigo que declara 2 punteros /*1*/ /*2*/ /*3*/ /*4*/ /*5*/ /*6*/ /*7*/ struct MyStruct { int m_aNumber; float num2; }; int * pJ2; struct MyStruct * pAnItem;

Las primeras 4 lneas definen la estructura. La linea 6 declara una variable que apuntar a un entero, y la lnea 7 declara una variable que apunta a algo de la estructura MyStruct. Entonces declarar un puntero es algo que apunta a algo de algn tipo, ms que contener el tipo. El asterisco (*) se coloca antes del nombre de la variable. En las siguientes lneas de cdigo, var1 es un puntero a un entero largo (long) mientras var2 es un entero largo (long) y no un puntero a un entero largo. En la segunda lnea se declara p3 como un puntero a un puntero de un entero. long int * var1, var2; ** p3;

Los punteros se usan habitualmente como parmetros de funciones. El siguiente cdigo muestra como declarar una funcin que usa un puntero como argumento. Teniendo en cuenta que C pasa todos los argumentos por valor, para poder modificar un valor desde el cdigo de la funcin, se debe usar un puntero al valor a modificar. Tambin se usan punteros a estructuras como argumentos de una funcin an cuando la estructura no sea modificada dentro de la funcin. Esto es realizado para evitar copiar todo el contenido de la estructura dentro de la pila. int MyFunction( struct MyStruct *pStruct );

Programacin en C/Punteros

59

Asignando valores a punteros


Continuamos con el proceso de asignar valores a los punteros, para esto utilizamos el operador & o 'la direccin de'. int myInt; int *pPointer; struct MyStruct struct MyStruct

dvorak; *pKeyboard;

pPointer = &myInt; pKeyboard = &dvorak; Aqu, pPointer apuntara a myInt y pKeyboard apuntara a dvorak. Los punteros tambin pueden ser asignados a referencias de memorias dinamicas creadas comnmente por las funciones malloc() y calloc(). #include <stdlib.h> ... struct MyStruct *pKeyboard; ... pKeyboard = malloc(sizeof(struct MyStruct)); ... La funcin malloc retorna un puntero de memoria asignada de manera dinmica (o NULL si falla). El tamao de esta memoria es definido de modo que pueda contener la estructura MyStruct El siguiente cdigo es un ejemplo mostrando un puntero siendo asignado a una referencia y se retorna el valor del puntero en la funcin. static struct MyStruct val1, val2, val3, val4; ... struct MyStruct *ASillyFunction( int b ) { struct MyStruct *myReturn; if (b == 1) myReturn = &val1; else if (b==2) myReturn = &val2; else if (b==3) myReturn = &val3; else myReturn = &val4; return myReturn; } ... struct MyStruct *strPointer; int *c, *d; int j; ... c = &j; operador & */ d = c; */

/* puntero asignado usando el /* asignando un puntero a otro

Programacin en C/Punteros strPointer = ASillyFunction( 3 ); /* puntero retornado de la funcion */ Cuando se retorna un puntero de una funcion, se tiene que tener cuidado de que el valor apuntado no sea de una referencia de una variable local a la funcion, porque estos valores desaparecen despues de salir de la funcion y el puntero estaria mal referenciado, causando errores en tiempo de ejecucion en el programa. La memoria creada dinamicamente dentro de la funcion puede devolverse como puntero, el valor de retorno aunque es creado en una variable local la direccion como tal se devuelve por valor dejando esta informacion valida.

60

Programacin en C/Manejo dinmico de memoria


Esta seccin no est lista si tienes algn conocimiento sobre este tema por favor, regstrate y contribuye con nuestro libro. Breve introduccin.

Memoria dinmica
Es memoria que se reserva en tiempo de ejecucin. Su tamao puede variar durante la ejecucin del programa y puede ser liberado mediante la funcin free.

Memoria esttica
Es el espacio de memoria que se crea al declarar variables, arrays o matrices de forma esttica (ej. int x, char c[MAX], etc) y cuyo tamao no podemos modificar durante la ejecucin del programa ni liberar el espacio que ocupa.

Diferencias, ventajas y desventajas


La memoria reservada de forma dinmica suele estar alojada en el heap o almacenamiento libre, y la memoria esttica en el stack o pila (con excepcin de los objetos de duracin esttica, que se vern ms adelante, los cuales suelen colocarse en una zona esttica de datos). La pila suele ser una zona muy limitada. El heap, en cambio, en principio podra estar limitado por la cantidad de memoria disponible durante la ejecucin del programa y el mximo de memoria que el sistema operativo permita direccionar a un proceso. La pila puede crecer de forma dinmica, pero esto generalmente depende del sistema operativo. En cualquier caso, lo nico que se puede asumir es que casi con seguridad dispondremos de menor espacio en la pila que en el heap. Otra ventaja de la memoria dinmica es que se puede ir incrementando durante la ejecucin del programa. Esto permite, por ejemplo, trabajar con arreglos dinmicos. Aunque en C, a partir del estndar C99 se permite la creacin de arreglos cuyo tamao se determina en tiempor de ejecucin, no todos los compiladores implementan este estndar. Adems, se sigue teniendo la limitante de que su tamao no puede cambiar una vez que se especifica, cosa que s se puede lograr asignando memoria de forma dinmica. Una desventaja de la memoria dinmica es que es ms difcil de manejar. La memoria esttica tiene una duracin fija, que se reserva y libera de forma automtica. En contraste, la memoria dinmica se reserva de forma explcita y contina existiendo hasta que sea liberada, generalmente por parte del programador. La memoria dinmica puede afectar el rendimiento. Puesto que con la memoria esttica el tamao de las variables se conoce en tiempo de compilacin, esta informacin est incluida en el cdigo objeto generado. Cuando se reserva memoria de manera dinmica, se tienen que llevar a cabo varias tareas, como buscar un bloque de memoria libre y

Programacin en C/Manejo dinmico de memoria almacenar la posicin y tamao de la memoria asignada, de manera que pueda ser liberada ms adelante. Todo esto representa una carga adicional, aunque esto depende de la implementacin y hay tcnicas para reducir su impacto.

61

El lenguaje C y el manejo de la memoria


Todos los objetos tienen un tiempo de vida, es decir, el tiempo durante el cual se garantiza que el objeto exista. En C, existen 3 tipos de duracin: esttica, automtica y asignada. Las variables globales y las variables locales declaradas con el especificador static tienen duracin esttica. Se crean antes de que programa inicie su ejecucin y se destruyen cuando el programa termina. Las variables locales no static tienen duracin automtica. Se crean al entrar al bloque en el que fueron declaradas y se destruyen al salir de ese bloque. Duracin asignada se refiere a los objetos cuya memoria se reserva de forma dinmica. Como se explic anteriormente, esta memoria se crea y se debe liberar de forma explcita. Los arreglos de longitud variable de C99 son un caso especial. Tienen duracin automtica, con la particularidad de que son creados a partir de su declaracin. La bilbioteca estndar de C proporciona las funciones malloc, calloc, realloc y free para el manejo de memoria dinmica. Estas funciones estn definidas en el archivo de cabecera stdlib.h.

malloc
La funcin malloc reserva un bloque de memoria y devuelve un puntero void al inicio de la misma. Tiene la siguiente definicin: void *malloc(size_t size); donde el parmetro size especifica el nmero de bytes a reservar. En caso de que no se pueda realizar la asignacin, devuelve el valor nulo (definido en la macro NULL), lo que permite saber si hubo errores en la asignacin de memoria. A continuacin se muestra un ejemplo de su uso: int *i; /* Reservamos la memoria suficiente para almacenar un int y asignamos su direccin a i */ i = malloc(sizeof(int)); /* Verificamos que la asignacin se haya realizado correctamente */ if (i == NULL) { /* Error al intentar reservar memoria */ } Uno de los usos ms comunes de la memoria dinmica es la creacin de vectores cuyo nmero de elementos se define en tiempo de ejecucin: int *vect1, n; printf("Nmero de elementos del vector: "); scanf("%d", &n); /* Reservar memoria para almacenar n enteros */ vect1 = malloc(n * sizeof(int); /* Verificamos que la asignacin se haya realizado correctamente */

Programacin en C/Manejo dinmico de memoria if (vect1 == NULL) { /* Error al intentar reservar memoria */ }

62

calloc
La funcin calloc funciona de modo similar a malloc, pero adems de reservar memoria, inicializa a 0 la memoria reservada. Se usa comnmente para arreglos y matrices. Est definida de esta forma: void *calloc(size_t nmemb, size_t size); El parmetro nmemb indica el nmero de elementos a reservar, y size el tamao de cada elemento. El ejemplo anterior se podra reescribir con calloc de esta forma: int *vect1, n; printf("Nmero de elementos del vector: "); scanf("%d", &n); /* Reservar memoria para almacenar n enteros */ vect1 = calloc(n, sizeof(int)); /* Verificamos que la asignacin se haya realizado correctamente */ if (vect1 == NULL) { /* Error al intentar reservar memoria */ }

realloc
La funcin realloc redimensiona el espacio asignado de forma dinmica anteriormente a un puntero. Tiene la siguiente definicin: void *realloc(void *ptr, size_t size); Donde ptr es el puntero a redimensionar, y size el nuevo tamao, en bytes, que tendr. Si el puntero que se le pasa tiene el valor nulo, esta funcin acta como malloc. Si la reasignacin no se pudo hacer con xito, devuelve un puntero nulo, dejando intacto el puntero que se pasa por parmetro. Al usar realloc, se debera usar un puntero temporal. De lo contrario, podramos tener una fuga de memoria, si es que ocurriera un error en realloc. Ejemplo de realloc usando puntero temporal: /* Reservamos 5 bytes */ void *ptr = malloc(5); /* Redimensionamos el puntero (a 10 bytes) y lo asignamos a un puntero temporal */ void *tmp_ptr = realloc(ptr, 10); if (tmp_ptr == NULL) { /* Error: tomar medidas necesarias */ } else { /* Reasignacin exitosa. Asignar memoria a ptr */ ptr = tmp_ptr; }

Programacin en C/Manejo dinmico de memoria Cuando se redimension la memoria con realloc, si el nuevo tamao (parmetro size) es mayor que el anterior, se conservan todos los valores originales, quedando los bytes restantes sin inicializar. Si el nuevo tamao es menor, se conservan los valores de los primeros size bytes. Los restantes tambin se dejan intactos, pero no son parte del bloque regresado por la funcin.

63

free
La funcin free sirve para liberar memoria que se asign dinmicamente. Si el puntero es nulo, free no hace nada. Tiene la siguiente definicin: void free(void *ptr); El parmetro ptr es el puntero a la memoria que se desea liberar: int *i; i = malloc(sizeof(int)); free(i); Una vez liberada la memoria, si se quiere volver a utilizar el puntero, primero se debe reservar nueva memoria con malloc o calloc: int *i = malloc(sizeof(int)); free(i); /* Reutilizamos i, ahora para reservar memoria para dos enteros */ i = malloc(2 * sizeof(int)); /* Volvemos a liberar la memoria cuando ya no la necesitamos */ free(i); Que es un buffer?

Buenas prcticas
Como se vio en las secciones anteriores, siempre que se reserve memoria de forma dinmica con malloc, realloc o calloc, se debe verificar que no haya habido errores (verificando que el puntero no sea NULL). Cuando se trata de verificar el valor de un puntero (y slo en ese caso), se puede usar de forma indistinta 0 NULL. Usar uno u otro es cuestin de estilo. Como ya se vio, las funciones de asignacin dinmica de memoria devuelven un puntero void. Las reglas de C establecen que un puntero void se puede convertir automticamente a un puntero de cualquier otro tipo, por lo que no es necesario hacer una conversin (cast), como en el siguiente ejemplo: /* El puntero void devuelto por malloc es convertido explcitamente a puntero int */ int *i = (int *)malloc(sizeof(int)); Aunque no hay un consenso, muchos programadores prefieren omitir la conversin anterior porque la consideran menos segura. Si accidentalmente se olvida incluir el archivo stdlib.h (donde estn definidas malloc, calloc, realloc y free) en un programa que use dichas funciones, el comportamiento puede quedar indefinido. Si omitimos la conversin explcita, el compilador lanzar una advertencia. Si, en cambio, realizamos la conversin, el compilador generar el cdigo objeto de forma normal, ocultado el bug.

Programacin en C/Manejo dinmico de memoria Una posible razn para usar la conversin explcita es si se escribe cdigo en C que se vaya a compilar junto con cdigo C++, ya que en C++ s es necesario realizar esa conversin. En cualquier caso, dado que el manejo de memoria es un tema complejo, y ste es un error muy comn, se debe hacer nfasis en que cuando se trabaja con memoria dinmica, siempre se debe verificar que se incluya el archivo stdlib.h. Tratar de utilizar un puntero cuyo bloque de memoria ha sido liberado con free puede ser sumamente peligroso. El comportamiento del programa queda indefinido: puede terminar de forma inesperada, sobrescribir otros datos y provocar problemas de seguridad. Liberar un puntero que ya ha sido liberado tambin es fuente de errores. Para evitar estos problemas, se recomienda que despus de liberar un puntero siempre se establezca su valor a NULL. int *i; i = malloc(sizeof(int)); free(i); i = NULL;

64

Fuentes y contribuyentes del artculo

65

Fuentes y contribuyentes del artculo


Programacin en C/Historia de C Fuente: http://es.wikibooks.org/w/index.php?oldid=166724 Contribuyentes: Almorca, Envite, Javier Carro, ManuelGR, Margamanterola, Maxy, X.Cyclop, 10 ediciones annimas Programacin en C/Fundamentos de programacin Fuente: http://es.wikibooks.org/w/index.php?oldid=158855 Contribuyentes: Almorca, Anakayama, Envite, Gargo, M.estarellas, ManuelGR, Margamanterola, Maxy, Rafael.galvez.vizcaino, Rodrigobgcr, X.Cyclop, 10 ediciones annimas Programacin en C/Primer programa en C Fuente: http://es.wikibooks.org/w/index.php?oldid=166727 Contribuyentes: Almorca, Dferg, Envite, Ikks, Ksaver, Luckas Blade, M.estarellas, ManuelGR, Margamanterola, Maxy, Morza, Rutrus, Url999, X.Cyclop, 12 ediciones annimas Programacin en C/Tipos de datos Fuente: http://es.wikibooks.org/w/index.php?oldid=166725 Contribuyentes: Alhen, Dferg, Don Quijote, Gargo, Javier Carro, Juliancolton, M.estarellas, ManuelGR, Margamanterola, Maxy, Rafael.galvez.vizcaino, Wutsje, 64 ediciones annimas Programacin en C/Interaccin con el usuario Fuente: http://es.wikibooks.org/w/index.php?oldid=154984 Contribuyentes: Gargo, M.estarellas, Maxy, 5 ediciones annimas Programacin en C/Expresiones Fuente: http://es.wikibooks.org/w/index.php?oldid=120849 Contribuyentes: Baldur, Gargo, Maxy, 2 ediciones annimas Programacin en C/Instrucciones de control Fuente: http://es.wikibooks.org/w/index.php?oldid=168017 Contribuyentes: Arpia49, CaStarCo, Envite, Fgrvpro, Gargo, Juanias, Luis449bp, ManuelGR, Margamanterola, Maxy, TheOrlSan, Zerohours, 82 ediciones annimas Programacin en C/Uso de funciones Fuente: http://es.wikibooks.org/w/index.php?oldid=162348 Contribuyentes: Dferg, Fgrvpro, Gargo, Luckas Blade, Maxy, Muro de Aguas, Zerohours, 7 ediciones annimas Programacin en C/Vectores Fuente: http://es.wikibooks.org/w/index.php?oldid=156849 Contribuyentes: Gallaecio, Gargo, Josemanuelmv, Kved, Maxy, Nahuel9000, 11 ediciones annimas Programacin en C/Cadenas de caracteres Fuente: http://es.wikibooks.org/w/index.php?oldid=123030 Contribuyentes: Maxy Programacin en C/Manejo de archivos Fuente: http://es.wikibooks.org/w/index.php?oldid=163664 Contribuyentes: Exe, Gargo, Irtusb, Jpag87a, Malonph, ManuelGR, Mchingotto, Morza, Zerohours, 44 ediciones annimas Programacin en C/Estructuras y Uniones Fuente: http://es.wikibooks.org/w/index.php?oldid=149417 Contribuyentes: Gargo, Taichi, Zerohours, 6 ediciones annimas Programacin en C/Punteros Fuente: http://es.wikibooks.org/w/index.php?oldid=143224 Contribuyentes: Jcongote, Luckas Blade, 8 ediciones annimas Programacin en C/Manejo dinmico de memoria Fuente: http://es.wikibooks.org/w/index.php?oldid=166700 Contribuyentes: CaStarCo, Gargo, Url999, 2 ediciones annimas

Fuentes de imagen, Licencias y contribuyentes

66

Licencia
Creative Commons Attribution-Share Alike 3.0 Unported http:/ / creativecommons. org/ licenses/ by-sa/ 3. 0/

Você também pode gostar