lunes, 30 de abril de 2012

Array en C. Parte 4: Operaciones con cadenas de caracteres...

En la entrada anterior iniciamos el tema de las cadenas de caracteres y por la extensión del post decidí explicar por separado algunas de las funciones que utilizaremos para trabajar con ellas y otras operaciones.

Así que manos a la obra....

Longitud de una cadena de caracteres:


Calcular la longitud de una cadena es una operación que se realiza con frecuencia. Si tendríamos que pensar una manera de implementarla lo más sencillo sería contar hasta llegar al final de la misma que está señalado por el caracter nulo ('\0'). Como siempre que queremos contar algo...

Una posible implementación de esto sería la siguiente:

Si vemos el código, nuestro i servirá para "caminar" dentro del while.

A través de una condición simplemente incrementamos i (gracias al operador de incremento) mientras no lleguemos al final de la cadena.
Y al final imprimimos en pantalla la longitud de la cadena que será i-1 ya que con nuestro i++ contaremos una vez adicional a lo que necesitamos.

Este es sólo un ejemplo, para implementarlo existen opciones de lo más variadas dependiendo del gusto de cada uno. Les recomiendo que prueben hacerlo con la estructura iterativa que más les agrade y decidan si usar un contador o no; verán que existe un mundo de posibilidades para ponerlo en práctica.

Para simplificar las cosas también existe una función de la librería string.h que se llama strlen que nos dará el mismo resultado. Strlen devuelve un entero que debemos asignar a una variable como se ve en la imagen:


Su uso no tiene complicaciones  pero recuerden que strlen cuenta de la misma forma que nosotros en el ejemplo anterior, por lo que recomendamos no usarlo dentro de otras iteraciones porque aumentaría mucho el tiempo de ejecución como veremos cuando expliquemos complejidad de algoritmos.

Copiar cadenas de caracteres:

Otra operación que se suele utilizar mucho es la de copiar el contenido de una cadena en otra. Esto se puede lograr con una estructura iterativa teniendo en cuenta dos detalles: primero que la segunda cadena donde guardaremos la copia tenga capacidad suficiente para guardar la primera (se recomienda darle la misma capacidad que ella) y que para copiar una cadena así hay que hacerlo caracter por caracter.

Si tenemos en cuenta esos detalles lograríamos realizar la copia con un código como éste:
Pero al igual que vimos para la longitud, también existe en string.h una función para el copiado de cadenas llamada strcpy. 

Usarla es tan sencillo como vemos en la imagen:

Pero hay que tener cuidado y asegurarnos que la copia tenga capacidad suficiente para guardar la cadena original, ya que strcpy no lo verifica y podría causar desbordamiento.

Otra opción sería usar strncpy, también incluida en string.h. Esta función hace lo mismo que strcpy pero tiene una limitación en el número máximo de caracteres que pueden copiarse. Veamos cómo usarla:


Recuerden dar a las cadenas una capacidad adecuada para evitar los problemas que podrían surgir al usar esta función.

Concatenar cadenas de caracteres:

Concatenar es simplemente unir una cadena con otra. Esto lo podríamos realizar con un bucle pero explicaremos directamente la manera de hacerlo con otra función de string.h llamada strcat.


Usarla es sencillo, en primer lugar pasamos como argumento la cadena que recibirá el resultado de la concatenación y en segundo la otra cadena. Como siempre hay que asegurarse de que la cadena que contendrá el resultado tenga capacidad para albergar ambas cadenas.

Si quisiéramos guardar la concatenación de A y B en otra cadena de nombre C deberíamos hacerlo en dos pasos:


Colocamos "" en C para que se coloque el caracter nulo al comienzo para simular que C está vacía y no haya problemas al concatenarla con A, si no podríamos obtener basura antes de la cadena como en la imagen:


Cuando deberíamos obtener esto:



Comparar cadenas de caracteres:

Las cadenas no se pueden comparar con los operadores que conocemos para comparar valores numéricos. Para poder realizarlo alfabéticamente existe una función de adivinen qué librería (si su respuesta fue string.h adivinaron) que se llama strcmp. Veamos cómo usarla y cómo interpretar el resultado que nos devuelve:

La imagen nos muestra que la forma de la función es muy simple, donde A y B son cadenas de caracteres . Pero escrita así no nos sirve de nada ya que la función devuelve un resultado entero y debemos asignarlo a alguna variable.

Los tres posibles resultados de strcmp pueden ser: 

  • -1 si A está antes que B en orden alfabético.
  • 0 si A y B son iguales.
  • 1 si A está después que B en orden alfabético.
Esto mismo lo ejemplificamos a continuación, mostrando un mensaje de acuerdo al caso que corresponda:



Aquí el caso será 1 ya que la p está después que la n en orden alfabético. 

String.h tiene muchas otras funciones que pueden utilizarse, pero las que explicamos hasta ahora son las más utilizadas. Si quieren probar alguna otra, pueden buscar la lista de funciones que nos ofrece esta librería y probarlas.

ALGUNAS FUNCIONES ÚTILES DE CTYPE.H:

La librería ctype.h también nos ofrece algunas funciones útiles para trabajar con caracteres. A continuación explicaremos algunas de ellas.

Obtener minúsculas o mayúsculas:

Para obtener la minúscula o mayúscula de un caracter usaremos las funciones tolower y toupper respectivamente. 

Si quisiéramos mostrar por pantalla un caracter en mayúscula y minúscula podríamos hacer lo siguiente:


Todo esto sin modificar el caracter original, sólo mostramos por pantalla lo que devuelven estas funciones.

Pero ya que estamos viendo cadenas de caracteres podríamos pensar que si usamos un bucle para recorrer uno por uno los caracteres de nuestra cadena podríamos transformarlos a minúsculas o mayúsculas con estas funciones:

Aquí estamos modificando la cadena original pasando todo a mayúsculas, que sería igual con tolower si quisiéramos transformar nuestra cadena a minúsculas. Si sólo quisiéramos mostrar en pantalla esto sin modificar la cadena bastaría con ir mostrándolas en el bucle directamente con printf sin hacer la asignación.

Preguntas acerca de caracteres:

Ctype.h también nos permite hacer preguntas acerca de los caracteres a través de varias funciones que mostraremos 


En todos los casos hace la pregunta que mostramos en la imagen, si lo es retorna verdadero y si no falso (Un caracter distinto de 0 si es verdadero y 0 si es falso). 

Como nos imaginamos, podríamos hacer estos test para toda una cadena evaluando cada caracter mediante un bucle; de igual forma que hicimos el pasaje a mayúsculas y minúsculas.


Llegamos al final de esta entrada, espero que les sirvan estas operaciones para trabajar con cadenas de caracteres, cualquier duda pueden colocarla como comentario o hacerla a través de nuestra página en facebook. Hasta la próxima.


domingo, 29 de abril de 2012

Array en C. Parte 3: Cadenas de caracteres...

En esta ocasión hablaremos acerca de las cadenas de caracteres en C (también llamadas strings) que son vectores de tipo char y que tendrán muchas particularidades respecto de los array que vamos viendo.

Una de las primeras particularidades que veremos es que toda cadena termina en el caracter nulo '\0' que en código ASCII vale 0, o también puede terminar en el entero 0 (sin comillas). Este caracter nos permitirá conocer el final de una cadena y modificarla sin problemas mientras respetemos la capacidad de la misma.

Declarando una cadena de caracteres:

Declarar una cadena no representa nada nuevo respecto a lo que ya vimos en entradas anteriores, ya que lo haremos como declararíamos un vector de tipo char.



La capacidad de la cadena es la cantidad de caracteres que podrá almacenar nuestra cadena. Si queremos guardar una determinada palabra o frase hay que darle una capacidad adecuada para ello y recordar que el caracter nulo ocupa el espacio de un elemento en ella.

Es importante recordar ese detalle y que la longitud de la cadena que guardemos no supere la capacidad de la misma ya que si no podríamos tener errores de todos los colores del arcoiris incluso sin que el compilador se de cuenta de ello.

Inicializando una cadena de caracteres:

Inicializaremos una cadena de caracteres de la siguiente manera:





En este ejemplo, no utilizamos la capacidad máxima de nuestra cadena ya que la palabra que almacenamos tiene una longitud menor. Si recordamos cómo se almacenaban en memoria los elementos de un vector y hacemos lo mismo para esta cadena de ejemplo tendríamos algo así:


Otra forma de inicializar nuestra cadena, sería hacerlo como vimos para vectores, siempre recordando colocar el caracter nulo al final y colocar cada caracter entre comillas simples:


O incluso si seguimos la lógica que nos indica que es un vector de caracteres podríamos inicializar cada elemento de nuestra cadena de acuerdo a su índice:

Aunque obviamente esto no es lo que recomendamos para inicializar una cadena es posible hacerlo así y hasta lo usaremos en algunas ocasiones en que necesitemos extraer un caracter específico de la misma.

SALIDA:

Usando printf con cadenas de caracteres:

Para mostrar por pantalla una cadena de caracteres usando printf, tendremos en cuenta dos nuevos detalles:

  • La marca de formato de una cadena de caracteres será %s.
  • Al indicar la cadena que mostraremos lo haremos con el nombre de la misma sin índice.
Con lo que usar printf para mostrar nuestra cadena de ejemplo será tan sencillo como poner :



Recordemos que nuestra cadena es un vector de tipo char, por lo que si queremos mostrar sólo un caracter en particular de la misma usaremos el índice y la marca de formato correspondiente (%c). Por lo que si quisiéramos mostrar sólo la j de la palabra ejemplo tendríamos:


ENTRADA:

Usando scanf para leer cadenas de caracteres:

Para leer cadenas de caracteres, scanf muestra su punto débil ya que sólo nos permite leer palabras y no oraciones completas, por lo que si ingresamos una frase sólo leerá hasta el primer espacio (blanco) de la misma.

Aún así mostraremos cómo leer una palabra a través de scanf en el siguiente ejemplo:


Y vemos que es igual de simple que con printf, sólo que notemos que colocamos sólo el nombre de la cadena sin el & ya que en una cadena el nombre guarda la dirección de memoria del primer elemento.

En caso que quisiéramos leer toda una cadena de caracteres con scanf deberíamos agregar varios scanf para ir leyendo cada palabra... en fin, nada óptimo ni mucho menos.

Usando gets para leer cadenas de caracteres:

Como vimos scanf muestra su debilidad ante las cadenas de caracteres, por lo que se necesita una función que pueda leer una cadena completa sin importar si es una palabra o toda una frase, otra función que puede utilizarse en este caso es gets que sí permite leer frases.

Si usar printf y scanf con cadenas de caracteres les pareció fácil, entonces usar gets les parecerá un juego de niños:

Se los dije... tan simple como escribir gets y pasar como argumento el nombre de la cadena. 

Pero no todo lo que brilla es oro y ésta no es la excepción. Gets también tiene sus problemas ya que no verifica que la cadena que ingresamos no sobrepase la capacidad de nuestra cadena, por lo que es una función que fácilmente puede provocar un buffer overflow (desbordamiento).

Usando getchar para leer cadenas de caracteres:

Para leer un solo caracter podemos utilizar getchar que es una función que debe asignarse a una variable de tipo char de la manera que vemos a continuación:

Por lo que si hacemos memoria y pensamos que en un array cada elemento con su índice se comporta como una variable, se nos podría venir a la mente la idea de realizar un bucle para leer una cadena completa usando getchar. 

Pero veamos ¿cómo podríamos implementarlo sin tener mil y un problemas? Porque si nos ponemos a pensar en todo lo que podría pasar sin condicionar esto tendríamos tema de sobra para hacer una película de terror para frikis.

A ver, pensemos todo lo que debemos tener en cuenta: la cadena no debe superar su capacidad incluyendo el caracter nulo y si presionamos enter se termina la cadena. 

Así que si creamos una variable char auxiliar y agregamos todas las condiciones necesarias podríamos conseguir lo deseado en este while:



Pero esto de tener que usar un while con tantas condiciones, cuántos i++, agregar el caracter nulo y cuánto más ya no es tan sencillo como vimos con otras funciones y encima cuando veamos complejidad de algoritmos veremos que con la manera anterior se nos va a las nubes comparado con usar scanf o gets.

Usando fgets para leer cadenas de caracteres:

Hasta ahora por uno u otro motivo ninguna función nos ayuda a leer una cadena de caracteres sin problemas y de manera sencilla, pero antes de darnos por vencidos volvamos a la esencia de lo que queremos hacer y analicemos.

Queremos ingresar una frase a través del teclado... pero el teclado en sí para C es lo que llamamos un fichero, explicando muy superficialmente y para que se entienda el fichero de entrada estándar stdin es lo que representa lo que llamamos teclado. Y es uno de los ficheros que se encuentran abiertos cuando ejecutamos nuestro programa. Éste se encuentra declarado dentro de stdio.h, cuando veamos más adelante el uso de ficheros veremos la sintaxis de esta declaración pero por ahora lo importante es que quede claro esto de que el teclado es un fichero.

Entonces si el teclado es un fichero podemos hacer uso de una función llamada fgets que nos permitirá leer cadenas de caracteres especificando el máximo tamaño que podrá tener y de esta manera evitar los problemas de desbordamiento que tanto nos vienen molestando en las otras funciones.

El uso de fgets vuelve a ser tan sencillo como las primeras opciones que vimos:

Vemos que fgets recibe como argumentos el nombre de la cadena, la capacidad de la misma y el fichero en cuestión. Por lo que esta función nunca leerá más caracteres de los que nos permite la capacidad de la cadena, incluyendo el caracter nulo (lee hasta llegar a ese límite o hasta que el usuario presione enter); lo que nos quita un problema de encima.

Por el momento no analizaremos más de esta función ya que eso queda para cuando veamos ficheros, pero con esto ya podemos leer cadenas de caracteres con más tranquilidad.



Ya que este post se extendió bastante llegaremos hasta aquí por ahora y en el próximo trataremos exclusivamente algunas de las funciones que utilizaremos para trabajar con estas cadenas de caracteres. 


Array en C. Parte 2: Matrices...

Luego de explicar vectores en C, vamos a referirnos a otra implementación de los array: las matrices.

Una matriz podría definirse como un vector multidimensional, es decir un vector de dos o más dimensiones. En ellas se trabaja de la misma manera que con los vectores, sólo que en lugar de un solo índice tendremos tantos como dimensiones de nuestra matriz.

Declarando una matriz:

Para declarar una matriz, haremos exactamente lo mismo que en vectores (véase: Parte 1) sólo que agregaremos tantos índices como dimensiones queremos que tenga nuestra matriz.

Por ejemplo, si queremos declarar una matriz1 de enteros de 3 dimensiones 2x4x6 o una matriz2 de flotantes 3x3, lo haríamos como en la imagen.



Inicializando los elementos de una matriz:


Para inicializar los elementos de una matriz también lo haremos de la misma manera que en vectores.

*Podemos inicializarlas elemento a elemento usando los índices:


Aquí inicializamos el elemento [0][1] de nuestra matriz en 0 y el elemento [1][2] en 3.

Hay que recordar que los elementos que no inicialicemos en ningún valor contendrán basura.




*Podemos usar estructuras de control iterativas para inicializar los elementos de nuestra matriz:

Hay que tener en cuenta que necesitaremos tantas estructuras de control iterativas como dimensiones tenga nuestra matriz.




En el ejemplo anterior, inicializamos todos los valores de nuestra matriz en 0.

*O podemos inicializar los elementos de manera explícita:


Esta inicialización se puede hacer en una sola línea pero lo colocamos de esta manera para que vean que se inicializan por fila.





Usando scanf y printf con matrices:

Utilizaremos estas funciones de la misma manera que explicamos para vectores.

En el siguiente ejemplo en lugar de pedir uno por uno los elementos con scanf lo hacemos dentro de tantas estructuras iterativas como dimensiones tiene nuestra matriz:








Y hacemos lo mismo para mostrar los elementos con printf en este ejemplo:








Sólo que agregamos algunos espacios después de cada elemento y un salto de línea entre cada fila para que se vea en pantalla como una matriz. Si inicializáramos los 9 elementos de nuestra matriz 3x3 con los números del 1 al 9 con ese ejemplo obtendríamos algo así:

















 Disposición en memoria de los elementos de una matriz:


Para explicar este tema, tomaremos como ejemplo una matriz 3x3 (como la de nuestros ejemplos). Si pedimos prestada la definición al álgebra lineal la definición de matriz y la armamos tendríamos algo como esto:


Donde, si nos ponemos a recordar un poco o simplemente miramos el gráfico, los subíndices de cada elemento indican el primero la fila y el segundo la columna correspondiente.

En esta forma, si queremos por ejemplo el elemento a23 simplemente buscamos el elemento que se encuentra en la fila 2 y en la columna 3.

Hasta aquí podrán decir que no hay ninguna novedad y que incluso ya trabajamos en los ejemplos con este concepto de matriz y que hasta hicimos un printf que la mostraba en este formato ¿entonces para qué esta explicación?

La respuesta es que en C las direcciones de memoria no son multidimensionales ni mucho menos, sino que se encuentran una al lado de la otra. Entonces veamos lo que hace el compilador en estos casos.

Cuando se encuentra con la definición de una matriz, el compilador reserva tantas direcciones de memoria contiguas como se necesite para guardar todos los elementos de nuestra matriz. En el caso de la matriz de enteros 3x3 reservará el espacio necesario para guardar 9 enteros.

Dejando de lado la cantidad de memoria que ocupa cada tipo de dato (tema que explicaremos en una de las próximas entradas) y explicándolo de manera muy simple; el compilador reservará esas 9 posiciones contiguas, es decir una al lado de la otra con lo que nuestra matriz 3x3 en realidad sería algo como esto:



Para encontrar cada elemento a través de los 2 índices el compilador hará la operación ((i*3)+j) con lo que veamos qué obtendría en los 9 casos:



Si miramos bien obtuvimos las posiciones de nuestro esquema anterior.


En realidad a ese resultado el compilador lo multiplica por la cantidad de bytes que ocupa el tipo de dato correspondiente y ubica las posiciones a partir de la dirección de memoria de inicio de la matriz almacenada en su nombre, pero como ya dijimos esto lo veremos mejor en otra entrada.









Y llegamos al final de esta entrada, en la siguiente nos sumergiremos en las aguas de las cadenas de caracteres en C y veremos nuevas librerías para trabajar con ellas. Hasta la próxima.


Array en C. Parte 1: Vectores ...

Llega el turno de hablar de uno de los tipos de datos derivados que llamamos array (o arreglo en español) y que usaremos con frecuencia en muchos programas y en la implementación de algunas estructuras de datos, entre otros.

Se suele dividir a los arreglos en tres categorías: vectores, matrices y cadenas de caracteres, aunque la esencia se mantiene y sólo hay pequeñas variantes entre cada una de ellas. En esta entrada nos ocuparemos de la primera y dedicaremos las próximas a explicar las dos restantes.

Primero lo primero: ¿Qué es un array?


Un array es un espacio de almacenamiento continuo donde guardaremos elementos del mismo tipo bajo un mismo nombre (identificador) y a los que podremos acceder por medio de índices que nos indicarán la posición de cada uno de esos elementos. También podríamos decir que es un conjunto de variables homogéneas (del mismo tipo).

 Para darnos una idea observemos el siguiente esquema:

Cada casillero corresponde a una dirección de memoria en la que guardaremos los elementos de nuestro array y se podrá acceder a esas direcciones por medio de los índices que irán desde 0 hasta el tamaño del arreglo menos uno.



Para recordar: Hay que tener presente que los arreglos son homogéneos, es decir que todos los elementos que contienen deben tener el mismo tipo de dato.

Más adelante, cuando lleguemos al nivel avanzado, veremos que podemos crear arreglos que contengan elementos de diferentes tipos e incluso crear arreglos dinámicos, pero por ahora trabajaremos con arreglos homogéneos estáticos.
  


Vectores:

Ahora que tenemos una idea de lo que es un array veamos la forma de implementarlo a través de lo que llamamos vector.

En C los vectores son conjuntos de elementos numéricos del mismo tipo. Si hacemos referencia al esquema anterior podemos decir que esos elementos que estarán almacenados en las direcciones de memoria de nuestro vector compartirán el mismo tipo de datos.

Declarando un vector:

La forma general para declarar un vector es la siguiente:


Donde el número de elementos (o tamaño) debe ser una expresión constante entera y lo demás será igual que en la declaración de una variable.

Por lo que si, por ejemplo, queremos crear un vector de enteros que tenga 10 elementos y que se llame VECTOR podríamos declararlo de la siguiente manera:


También podríamos definir una constante  para declarar este vector:


Hay que notar que lo que hace el compilador en estas declaraciones es reservar espacios de memoria consecutivos para 10 elementos de tipo entero.

¿Qué contiene un vector sin inicializar?:

Una vez definido nuestro vector, alguien podría imaginarse que está vacío o que todos sus elementos están en 0, pero en realidad contiene lo que llamamos basura en cada una de sus posiciones de memoria. Veamos esto con un ejemplo:

Declaramos un vector y, mediante una iteración, utilizamos los índices para mostrar en pantalla el contenido de nuestro vector al que dejamos sin ningún elemento:

Ahora compilamos nuestro programa, lo ejecutamos y vemos qué contiene nuestro vector:


Y vemos claramente a lo que nos referimos con basura... tiene cualquier cosa, lejos de estar todo en cero.

Inicializando elementos en un vector:

Ahora que vimos lo que contiene un vector sin elementos, veamos cómo inicializar los elementos que queremos en nuestro vector.

Si vieron el código que mostraba lo que tenía el vector vacío y ahora suponen que para hacer esta inicialización debemos usar los índices: ¡BINGO! acertaron.

Y es que realmente es tan fácil como hacer una simple asignación teniendo en cuenta el índice. Veamos el ejemplo:

Aquí podemos ver que podemos asignar directamente un elemento del tipo correspondiente al índice en donde queremos guardarlo e incluso podemos operar con el nombre de vector y el índice del elemento como si fueran variables de ese tipo.

También podemos inicializar los elementos de nuestro vector mediante una estructura de control iterativa, por ejemplo si queremos colocar en 0 todos los elementos de nuestro vector de ejemplo, podríamos hacer lo siguiente:


Una opción alternativa que tenemos para hacer esto es realizar la inicialización de todos los elementos de la siguiente manera:


En este ejemplo, declaramos e inicializamos simultáneamente nuestro vector con los números del 0 al 9. En esta forma, los elementos que "guardamos" se asignan a las direcciones de memoria correspondientes empezando desde la primera.

 Esta forma de inicializar sólo la recomendamos cuando se trabaja con vectores de pocos elementos.

Usando scanf y printf con vectores:

En caso que queramos que el usuario ingrese algún elemento de nuestro vector o imprimir en pantalla el elemento de un vector lo haremos de la misma manera que ya vimos para variables sólo que siempre debemos recordar utilizar el índice.

Por ejemplo si queremos ingresar y mostrar por pantalla el elemento de índice 1 de VECTOR, haríamos lo siguiente, utilizando la marca de formato correspondiente al tipo de nuestro vector:



Hasta aquí llegamos con la explicación acerca de los vectores en C, en las próximas entradas veremos un poco más acerca de los array, en especial sobre matrices y cadenas de caracteres.




sábado, 28 de abril de 2012

C: Funciones...

Anteriormente realizamos varios post de introducción a C donde pudimos adentrarnos en el mundo de este lenguaje adquiriendo las nociones básicas para codificar algunos programas sencillos. Pero, con ello, recién recorrimos la primera parte del camino y queda mucho por aprender.

Entre todos los temas que quedan por delante, nos encontramos con las funciones que, como vimos en pseudocódigo son subprogramas, es decir que son porciones de código independientes del programa principal que pueden recibir argumentos y devuelven un solo resultado (a excepción de las funciones void a las que dedicaremos una parte de esta entrada).

Ventajas del uso de funciones:

Las funciones nos serán de mucha utilidad ya que podremos programar de manera modular. Gracias a esta manera de programar podremos aplicar aquella idea de refinamiento que venimos viendo desde algoritmos y obtener como resultado un código mucho más claro y reutilizable.

Aunque esto de reutilizable suene a una campaña de reciclaje, es una de las grandes ventajas de usar funciones ya que podremos volver a usarlas en diferentes programas sin necesidad de volver a codificarlas. Y no sólo eso, sino que además podremos llamarlas todas las veces que las necesitemos lo que conllevaría gran cantidad de líneas de código repetidas si deberíamos implementarlo sin funciones en un mismo programa.

Prototipo y definición de una función:

Ahora que ya sabemos lo que son las funciones y las ventajas de usarlas, veamos cómo implementarlas en nuestros programas en C.

Prototipo:

En primer lugar tenemos el prototipo de la función que le dará información al compilador sobre el tipo de retorno  y detalles sobre los argumentos de la misma como el tipo de dato, la cantidad y el orden en que se los pasaremos a nuestra función. Esto le sirve al compilador para hacer las comprobaciones correspondientes en las llamadas a las funciones, dando error en caso que no nos ciñamos al prototipo. También el compilador intentará hacer conversiones de tipo de dato si el que pasamos en la llamada no corresponde con el tipo del argumento que habíamos especificado en el prototipo, esto siempre y cuando se pueda realizar.

El prototipo de una función en C, tendrá el siguiente aspecto:


Como podemos apreciar, se coloca antes de la función main y tiene el formato que se ve en la imagen.



Un ejemplo de un prototipo sería el que vemos a la izquierda. Donde tenemos una función de nombre "funcion1" que retornará un dato de tipo entero y que tendrá como argumentos dos enteros.





Definición:

La definición de nuestra función será la función en sí, osea el código de la misma y tiene la siguiente estructura:


Las definiciones de todas las funciones se colocan luego de la función main. 

Primero se coloca lo que se llama encabezamiento (o header) que, si vemos, es muy similar al prototipo, sólo que en este caso se coloca los nombres de los parámetros de la función (dato1 y dato2 en este caso). 



Dentro de las llaves que delimitan la función encontraremos todas las expresiones que forman parte de la misma, incluyendo la declaración de variables locales.

Al final se utiliza la palabra reservada return para indicar la expresión o variable que retornará nuestra función, este retorno deberá ser del tipo que indicamos en el prototipo, en nuestro ejemplo int.


Diferencia entre parámetros y argumentos: Como nota, agregamos esta aclaración para quien no tenga claras las diferencias entre los mismos. Aunque en algunos lugares se toman como sinónimos, los parámetros son los que aparecen en el prototipo y la definición de la función,  mientras que los argumentos corresponden al valor que le pasamos a la función cuando realizamos la llamada a la misma.

En alguna bibliografía a los parámetros se los llama con el nombre de "argumentos formales" y los argumentos reciben el nombre de "argumentos actuales". 
  

Llamadas a funciones:

El llamado a una función dentro de nuestro programa lo haremos de la misma manera que vimos en pseudocódigo.

Si la función retorna un valor (no es void) podemos asignar ese valor a una variable:


Suponiendo que variable, x e y son enteros, podemos asignar el resultado de nuestra función ejemplo a variable.



 En la imagen lo mostramos de manera general, pero x e y podrían ser variables ingresadas por el usuario anteriormente, números enteros que pasemos directamente como argumentos, resultados de expresiones que devuelvan un tipo entero, etc.



También podemos utilizar el valor que retorne nuestra función en estructuras de control como ejemplificamos en la imagen de la izquierda a través de una estructura if.






Funciones void:

Dentro de las funciones, hay un tipo especial a las que llamamos funciones de tipo void o simplemente funciones void que constituyen lo que habíamos mencionado alguna vez como procedimientos. Estas funciones tienen todas las características de una función sólo que su tipo de retorno es void y no retornan ningún valor.


El prototipo de estas funciones será igual que cualquier otra, sólo que tendrá la palabra reservada void como tipo de retorno.


La definición tendrá la misma estructura que vimos anteriormente, sólo que no retornará ningún valor.





Y simplemente las llamaremos con su nombre y argumentos;