Vous êtes sur la page 1sur 32

Las funciones en C

Tabla de contenidos

1. Definición de funciones
2. Paso de parámetros a una función
Tablas como parámetros a una función
3. Prototipos de funciones
4. Funciones estáticas
5. Ejercicios

El código de un programa escrito en C se divide en funciones. Aunque


similares a los “métodos” de Java, las funciones no están asignadas
ni a una clase ni a un objeto. Una función en C se distingue sólo por
su nombre. Dos funciones con igual nombre y con diferente número y
tipo de parámetros se considera una definición múltiple, y por tanto
un error.

Las funciones suelen encapsular una operación más o menos


compleja de la que se deriva un resultado. Para ejecutar esta
operación, las funciones pueden precisar la invocación de otras
funciones (o incluso de ellas mismas como es el caso de las funciones
recursivas).

Las funciones en un programa son entidades que dado un conjunto


de datos (los parámetros), se les encarga realizar una tarea muy
concreta y se espera hasta obtener el resultado. Lo idóneo es dividir
tareas complejas en porciones más simples que se implementan
como funciones. La división y agrupación de tareas en funciones es
uno de los aspectos más importantes en el diseño de un programa.

1. Definición de funciones
Las funciones en C tienen el siguiente formato:

tipo_del_resultado NOMBRE(tipo_param1 param1, tipo_param2


param2, ... )
{
/* Cuerpo de la función */
}

Cuando se invoca una función se asignan valores a sus parámetros y


comienza a ejecutar el cuerpo hasta que se llega al final o se
encuentra la instrucción return. Si la función devuelve un
resultado, esta instrucción debe ir seguida del dato a devolver. Por
ejemplo:
1 int search(int table[], int size)
2 {
3 int i, j;
4 if (size == 0)
5 {
6 return 0;
7 }
8 j = 0;
9 for (i = 0; i < size; i++)
10 {
11 j += table[i];
12 }
13 return j;
14 }

La ejecución de la función comienza en la línea 4. Si el


parámetro size tiene valor cero, la función termina y devuelve el
valor cero. Si no, se ejecuta el bucle y se devuelve el valor de la
variable j. El tipo de la expresión que acompaña a la
instrucción return debe coincidir con el tipo del resultado declarado
en la línea 1.

La llamada a una función se codifica con su nombre seguido de los


valores de los parámetros separados por comas y rodeados por
paréntesis. Si la función devuelve un resultado, la llamada se
reemplaza por su resultado en la expresión en la que se incluye. Por
ejemplo:

1 int addition(int a, int b)


2 {
3 return (a + b);
4 }
5 int main()
6 {
7 int c;
8 c = c * addition(12, 32);
9 }
2. Paso de parámetros a una función
Los parámetros son variables locales a los que se les asigna un valor
antes de comenzar la ejecución del cuerpo de una función. Su ámbito
de validez, por tanto, es el propio cuerpo de la función. El mecanismo
de paso de parámetros a las funciones es fundamental para
comprender el comportamiento de los programas en C.

Considera el siguiente programa:

1 int addition(int a, int b)


2 {
3 return (a + b);
4 }
5 int main()
6 {
7 int x = 10;
8 int y = 20;
9 int z;
10
11 z = addition(x, y);
12 }

Los parámetros a y b declarados en la línea 1 son válidos únicamente


en la expresión de la línea 3. Las variables x, y y z, por su lado, son
válidas en el cuerpo de la función main (líneas 7 a 11).

El ámbito de las variables x, y y z (ámbito llamador), y el de las


variables a y b (ámbito llamado) son totalmente diferentes. El ámbito
llamador desaparece temporalmente cuando se invoca la función
desde la lińea 11. Durante esa ejecución, el ámbito llamado es el
visible. Al terminar la función, el ámbito llamado desaparece y se
recupera el ámbito llamador.

La comunicación entre estos dos ámbitos se realiza en la línea 11.


Antes de comenzar la ejecución de la función, los valores de las
variables del ámbito llamador son copiadas sobre las variables
del ámbito llamado. Cuando termina la ejecución de la función, la
expresión de la llamada en la línea 11 se reemplaza por el valor
devuelto. En la siguiente figura se ilustra este procedimiento para el
ejemplo anterior.
El paso de parámetros y la devolución de resultado en las
funciones C se realiza por valor, es decir, copiando los
valores entre los dos ámbitos .

Las variables locales de la función (incluidos los parámetros)


desaparecen al término de la función, por lo que cualquier valor que
se quiera guardar, debe ser devuelto como resultado (ver
ejercicio 1).

Tablas como parámetros a una función

La copia de parámetros del ámbito llamador al llamado tiene una


excepción. Cuando una función recibe como parámetro una tabla, en
lugar de realizarse un duplicado se copia su dirección de memoria.
Como consecuencia, si una función modifica una tabla que recibe
como parámetro, estos cambios sí son visibles en el ámbito llamador.
El siguiente ejemplo ilustra esta situación:

1 #define NUMBER 100


2
3 void fill(int table[NUMBER], int size)
4 {
5 int i;
6 for (i = 0; i < size; i++)
7 {
8 table[i] = 0;
9 }
10 }
11
12 int main()
13 {
14 int i, data[NUMBER];
15
16 for (i = 0; i < size; i++)
17 {
18 data[i] = 10;
19 }
20 fill(data, NUMBER);
21 /* Valores de data todos a cero */
22 }

Para la explicación de este comportamiento es preciso primero


comprender el mecanismo de punteros en C.

3. Prototipos de funciones
Como el compilador trabaja sólo con la información contenida en un
único fichero, a menudo es preciso “informar” al compilador de que
en otro fichero existe una función. Esto se consigue insertando, en
lugar de la definición de la función (que ya está presente en otro
fichero), su prototipo. El prototipo de una función es una línea similar
a la primera de su declaración: tipo del resultado, seguido del nombre
de la función y de la lista de tipos de datos de los parámetros
separados por comas y rodeados por paréntesis. Toda función que se
invoca debe ir precedida o de su definición o de su prototipo. La
definición y el prototipo de la función pueden estar presentes en el
mismo fichero. El siguiente ejemplo ilustra esta situación:

1 /* Prototipo */
2 int addition(int, int);
3 /* Función principal */
4 int main()
5 {
6 int i, j;
7
8 i = 10;
9 j = 20;
10 /* Invocación de la función */
11 i += addition(i, j);
12 }
13 /* Definición de la función */
14 int addition(int a, int b)
15 {
16 return (a + b);
17 }

Sugerencia

Copia y pega el ejemplo anterior en un fichero de texto en tu entorno


de desarrollo y compila el programa con el comando gcc -Wall -o
programa fichero.c reemplazando fichero.c por el nombre del
fichero que hayas utilizado. Si el compilador no imprime ningún
mensaje quiere decir que el programa es sintácticamente correcto.
Prueba a quitar el prototipo de la línea 2 y compila de nuevo. Si
mueves la definición de la función que comienza en la línea 14 al
principio del fichero, ¿necesitas el prototipo? ¿qué pasa si aún así lo
incluyes? ¿qué sucede si la función y el prototipo no coinciden en el
tipo de los parámetros o el tipo del resultado?

4. Funciones estáticas
La definición de una función puede tener el prefijo “static”. Cuando
una función se declara como estática, tan sólo puede ser invocada
desde el fichero en el que está definida. Este mecanismo, por tanto,
puede interpretarse como una forma primitiva de restringir el acceso
a una función, pero dista mucho del mecanismo de tres niveles
(público, privado y protegido) presente en Java.

Cuando se desarrollan aplicaciones de gran tamaño, se suelen


establecer políticas para el uso de prototipos. Por ejemplo, para
poder invocar cualquier función en cualquier parte del código de un
fichero, se suelen colocar al comienzo del mismo los prototipos de
todas las funciones que contiene. El siguiente ejemplo muestra un
ejemplo de esta política:

1 /* Funciones globales */
2 int function1(int p1, float p2, int table[], int
3 size);
4 void function2();
5 struct data *function3(char *name, char* lastname,
int status);
6
/* Funciones locales */
7
8 static void check(int table[], int size);
9 static struct data *duplicate(struct data *d1);
10
/* Definiciones */
....

5. Ejercicios
1. Un programador con poca experiencia nos manda la siguiente
función que dice que intercambia el valor de sus dos
parámetros. Si se llama como swap(x, y), al terminar la
ejecución, las variables x e y han intercambiado sus valores.
¿Es eso cierto? Comprueba tu respuesta creando un programa
en tu entorno de desarrollo y ejecutándolo. Puedes imprimir la
variable entera x con la líneaprintf("%d\n", x);.

1 void swap(int a, int b)


2 {
3 int tmp;
4 tmp = a;
5 a = b;
6 b = tmp;
7 return;
8 }

2. De nuevo este mismo programador nos manda una segunda


función que dada una tabla de números enteros incrementa en
uno cada uno de sus elementos. ¿Crees que esta función hace
realmente esto? (Pista: la definición de la función es correcta,
no tiene error alguno de sintaxis).

1 void increase(int table[], int size)


2 {
3 int i;
4 for (i = 0; i < size; i++)
5 {
6 table[i]++;
7 }
8 }

3. Una aplicación manipula datos obtenidos de un sistema de


posicionamiento global a través de la siguiente estructura:

1 struct gps_information
2 {
3 int is_3D;
4 float latitude;
5 float longitude;
6 float height;
7 };
8 typedef struct gps_information gps_info;

Escribe el prototipo de una función que recibe dos estructuras


de este tipo y un float. La función devuelve un entero con
valor 1 si latitud y longitud de ambas estructuras están
separadas por no más del valor del tercer parámetro y cero en
caso contrario. Debes utilizar el sinónimo de tipo de datos que
se define.

Escribe la función con su definición completa (quizás te sea de


ayuda la función de librería fabsf).

4. Escribe una función que recibe como parámetros dos tablas de


estructuras como las del ejercicio anterior, un entero con su
longitud (que es la misma para ambas), y un float. La
función procesa las dos tablas a la vez (primero con primero,
segundo con segundo, etc.) y devuelve el índice del primer par
de elementos que están cerca tal y como se calcula con la
función del apartado anterior utilizando el último parámetro
como distancia. Si la condición no se cumple para ningún par de
elementos, devuelve el valor -1.

Esta función que has implementado es la que se necesita en la


aplicación, y no la anterior, que es una función auxiliar y de
ámbito reducido. ¿Qué cambio harías en la definición del
ejercicio anterior?

Funciones[editar]
Como vimos anteriormente C tiene como bloque básico la función main() , también
hemos visto la sentencia printf() que es otra función, y de igual forma hay muchas más
funciones predefinidas, pero nosotros mismos también podemos definir nuestras propias
funciones. De hecho, es fundamental hacerlo.
Podemos definir una función cualquiera de la misma manera en que definimos la
función main() . Basta con poner su tipo, su nombre, sus argumentos entre paréntesis y
luego, entre llaves, su código:

/* Inclusión de archivos */
#include <stdio.h>

void holamundo(void) /* Función donde se ejecuta la lógica del


programa */
{
printf("Hola Mundo\n"); /* imprime la cadena */
return; /* sale de la función */
}

int main(void) /* Función principal del programa */


{
holamundo(); /* llamada a la función holamundo */
return 0; /* sale del programa con código 0 (correcto) */
}

Este código es en todo equivalente al "Hola Mundo" original, sólo que nos muestra cómo
escribir y cómo utilizar una función. Y además nos muestra un principio de buena
programación: meter las sentencias que "hacen el trabajo" en otras funciones específicas
para sacarlas de main() , dejando en ésta tan sólo un guión general de lo que hace el
programa, no las órdenes específicas. De esta manera se facilita la comprensión del
programa, y por tanto el futuro trabajo de modificarlo.

La sentencia return [editar]

La sentencia return puede utilizarse dentro de una función para terminar su ejecución.
En el ejemplo anterior, la función holamundo fue declarada con valor de retorno de
tipo void (es decir, valor de retorno nulo). En ese caso, la sentencia return no lleva
ningún parámetro adicional, ya que la función no debe devolver ningún valor a la función
que la llama.
En cambio, la función main tiene un valor de retorno de tipo int , por lo
que return debe ir seguido de un valor entero (0 en el ejemplo). El valor 0 se utiliza para
indicar que el programa ha llegado a un punto en el que todo se ha desarrollado
correctamente y se utiliza cualquier otro valor para indicar que ha habido algún tipo de
error.
La instrucción return no es una función, se trata de una sentencia que lo que hace es
retornar como valor de la función el valor que se le proporciona como argumento.

Argumentos[editar]
Las funciones también pueden recibir argumentos o parámetros, para modificar su
comportamiento. Por ejemplo, la definición de una función para sumar dos números sería
de la siguiente manera:

Declaración y definición[editar]
En el ejemplo anterior podemos notar que la función sumar figura en el código antes
que main . ¿Qué pasaría si las escribiéramos en distinto orden?

#include <stdio.h>

int main(void)
{
int suma = sumar(5, 3); /* ERROR, sumar no ha sido declarada
aún */
printf("La suma es: %d ", suma);
return 0;
}

int sumar(int numero1, int numero2)


{
return numero1 + numero2;
}

En este caso el programa es erróneo y no compila, ya que en la línea donde se llama a la


función sumar , el compilador aún no conoce ninguna función con ese nombre, y cuáles
son sus argumentos y valor de retorno.
Una posible solución es declarar el prototipo de la función al principio, para informar al
compilador que existe, y luego definir el cuerpo de la misma en cualquier lugar del
programa:

#include <stdio.h>

/* Declaración */
int sumar(int numero1, int numero2);

int main(void)
{
int suma = sumar(5, 3);
printf("La suma es: %d ", suma);
return 0;
}

/* Definición */
int sumar(int numero1, int numero2)
{
return numero1 + numero2;
}

Paso de Parámetros[editar]
Las funciones pueden recibir datos como lo hemos observado, pero existen dos formas de
enviar los datos hacia una función por valor y por referencia, las cuales modifican en
diferente forma el comportamiento del programa.

Por Valor[editar]
El paso por valor envía una copia de los parámetros a la función por lo tanto los cambios
que se hagan en ella no son tomados en cuenta dentro de la función main() . Ejemplo:

/*
* por_valor.c
*
* Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
*
* para el wikilibro "Programación en C"
* bajo licencia FDL, adaptado del Dominio Público
*/

#include <stdio.h>

void sumar_valor(int numero); /* prototipo de la función */

int main(void)
{
int numero = 57; /* definimos numero con valor de 57*/

sumar_valor(numero); /* enviamos numero a la función */

printf("Valor de numero dentro de main() es: %d\n", numero);


/* podemos notar que el valor de numero se modifica
* sólo dentro de la función sumar_valor pero en la principal
* número sigue valiendo 57
*/

return 0;
}

void sumar_valor(int numero)


{
numero++; /* le sumamos 1 al numero */
/* el valor de número recibido se aumenta en 1
* y se modifica dentro de la función sumar_valor()
*/
printf("Valor de numero dentro sumar_valor() es: %d\n",
numero);

return;
}

Por Referencia[editar]
El paso por referencia se hace utilizando apuntadores. Se envía la dirección de memoria
de la variable, por lo tanto los cambios que haga la función si afectan el valor de la
variable. Ejemplo:

/*
* por_referencia.c
*
* Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
*
* para el wikilibro "Programación en C"
* bajo licencia FDL, adaptado del Dominio Público
*/

#include <stdio.h>

void sumar_referencia(int *numero); /* prototipo de la función */

int main(void)
{
int numero = 57; /* definimos numero con valor de 57*/

sumar_referencia(&numero); /* enviamos numero a la función */

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() también se ha modificado
* aunque la función no haya retornado ningún 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 función
*/
printf("\nValor de numero dentro sumar_referencia() es: %d",
*numero);

return;
}

Variables Locales y Globales[editar]


Además de pasar valores a una función, también se pueden declarar tipos de datos dentro
de las funciones, estos tipos de datos declarados dentro de una función solo son
accesibles dentro de esta misma función 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 César Brizuela <brizuelaalvarado@gmail.com> 2009
*
* para el wikilibro "Programación en C"
* bajo licencia FDL, adaptado del Dominio Público
*/

#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 función principal main() y fuera de
cualquier otra función 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
función creada por nosotros. Ejemplo:

/*
* global.c
*
* Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
*
* para el wikilibro "Programación en C"
* bajo licencia FDL, adaptado del Dominio Público
*/

#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 función */
funcion();

return 0;
}

void funcion()
{
/* imprimimos el valor*/
printf("funcion(), acceso a variable_global %d\n",
variable_global);

return;
}

Funciones Recursivas[editar]
La recursividad (recursión) es la propiedad por la cual una función se llama a sí
misma directa o indirectamente. La recursión indirecta implica utilizar más de una
función.
Se puede considerar la recursividad como una alternativa a la iteración. La recursión
permite especificar soluciones naturales, sencillas, que serían, en caso contrario, difíciles
de resolver. Toda función recursiva debe contemplar un caso base o condición de
salida, para terminar, o la recursividad no podrá terminar nunca.
Una función recursiva podría definirse así:

funcion_recursiva( /* parámetros recibidos por la función */ )


{
/* Código */
funcion_recursiva( ); /* llamada a la función misma */
/* Código */
}

Uno de los ejemplos más representativos en la recursividad es el factorial de un numero (


n! ):

la definición de recursividad del factorial es:

En esta definición, n = 0, es nuestro caso base, que le da fin a la recursividad.


Entonces nuestro programa que calcula el factorial es:

/*
*factorial.c
*
* Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
*
* para el wikilibro "Programación en C"
* bajo licencia FDL, adaptado del Dominio Público
*/

#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
función */
}

int main(void)
{
/* en este caso se llama a la función y se imprime
directamente*/
printf("%ld ", factorial(5));

return 0;
}

También existen otros tipos de funciones recursivas como lo es el producto de dos

números. El producto de a b, donde a y b son números enteros positivos


seria:
Solución iterativa:

Solución recursiva:

Así pues es:


Podemos ver que la multiplicación de dos números a, b se puede transformar
en otro problema más pequeño multiplicar a por (b-1), el caso base se
produce cuando b = 0 y el producto es 0. Ejemplo:

/*
* producto.c
*
* Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
*
* para el wikilibro "Programación en C"
* bajo licencia FDL, adaptado del Dominio Público
*/

#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 función */
}

int main(void)
{
/* en este caso se llama a la función y se imprime
directamente*/
printf("%i ", producto( 7, 3));

return 0;
}

Recursividad indirecta o recursión mutua[editar]


Esta se produce cuando una función llama a otra, que esta a su vez terminará
llamando de nuevo a la primera función. El siguiente programa visualiza el
alfabeto utilizando recursión indirecta o mutua:

/*
* elalfabeto.c
*
* Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
*
* para el wikilibro "Programación en C"
* bajo licencia FDL, adaptado del Dominio Público
*/

#include <stdio.h>

void funcionA(char c); /* se declara el prototipo de la


función para que el llamado */
void funcionB(char c); /* a la misma en la función no sea
implícita */

int main(void)
{

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' */
}

Recursión versus Iteración[editar]


Tanto la iteración como la recursión se basan en estructura de control: la
iteración utiliza una estructura repetitiva y la recursión una estructura de
selección. La iteración utiliza explícitamente una estructura repetitiva mientras
que la recursión consigue la repetición mediante llamadas repetitivas a
funciones.
La iteración termina si la condición del bucle no se cumple, mientras que la
recursión termina cuando se reconoce un caso base.
La recursión puede presentar desventajas ante la iteración ya que se invoca
repetidas veces al mecanismo de llamada de funciones y se necesita un
tiempo mayor para realizar cada llamada.
La razón 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 fáciles de implementar.
Ejemplo Iterativo[editar]

/*
* iterativo.c
*
* Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
*
* para el wikilibro "Programación en C"
* bajo licencia FDL, adaptado del Dominio Público
*/

#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 ));

return 0;
}

/* funcion factorial iterativa */


long factorial( int numero )
{
long resultado = 1;
int i = 0;

/* declaracion de la función factorial iterativa */


for ( i = numero; i >= 1; i-- )
resultado *= i;

return resultado;
}

Ejemplo Recursivo[editar]
/*
* recursivo.c
*
* Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
*
* para el wikilibro "Programación en C"
* bajo licencia FDL, adaptado del Dominio Público
*/

#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 ));

return 0;
}

/* función 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 función factorial */
}

Programación en C/Vectores
< Programación en C

← Uso de funciones 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 posición asociada
que se usará para accederlos. Está posición o índice será siempre un número 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 irían 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 también 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 información, 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 través de su posición. Es decir:

tipo_elemento elemento;
...
elemento = nombre[2];

Asumiendo que tenemos el vector anterior definido estaríamos


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 matemáticos y


calcular el producto escalar entre ellos. Una peculiaridad que se puede notar es que al
recibir un arreglo en una función no se especifica el largo, volveremos a esto en un
capítulo posterior.
Otra función clásica es la búsqueda de un máximo o mínimo, 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]) {
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 función recibe un vector por parámetro y cambia su contenido y el cambio es
permanente (se ve aún fuera de la función). Esto puede parecer extraño después del
énfasis que pusimos en resaltar que todos los parámetros de una función se reciben por
valor, pero se aclarará en el siguiente capitulo.
Mientras tanto usemos esto para definir una función que le aplique otra función que recibe
por parámetro 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 básicos, 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 líneas 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
...

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 posición siempre será v[0]


2. La última posición es v[n-1]
3. En versiones previas a C99 n es una constante definida antes de la declaración de
v[n]

Programación en C/Cadenas de caracteres


< Programación en C

← Vectores Cadenas de caracteres

Las cadenas de caracteres (también llamadas cadenas o strings) son un tipo particular de
vectores, que como su nombre lo dice son vectores de char, con la particularidad que
tienen una marca en el fin del (el caracter '\0'), además el lenguaje nos permite escribirlas
como texto dentro de comillas dobles si son simples no. Veamos unos ejemplos de su
declaración:

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[]="";

Cómo 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.
También 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 parámetro más 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 función 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;
}
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
estándar de C a través del encabezado string.h. Nuestro largo_cadena() es similar
al strlen()de la biblioteca estándar, 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 estándar, ya que podemos estar seguros que van
a estar programadas de la mejor manera posible.
Entre las funcione que provee la biblioteca estándar de C, las más 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 grande
resultado = strcmp(cadena1, cadena2) // Compara dos cadenas
// devuelve un valor menor, igual o mayor que 0 según si
cadena1 es menor,
// igual o mayor que cadena2, respectivamente.
posicion = strchr(cadena, caracter) // Devuelve la posición en memoria
de la primer
// aparición de caracter dentro de cadena
posicion = strstr(cadena,subcadena) // Devuelve la posición en memoria
de la primer
// aparición 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);
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;
}

1. Funciones que no devuelven ningún valor.

Cómo se ha dicho las funciones pueden o no devolver algún valor, para mi


parecer, este tipo de funciones son las más sencillas, ya que cuando se llama la
función, esta realiza lecturas, asignaciones, cálculos o impresiones, finaliza la
ejecución de la función y el programa continúa normalmente.
Ejemplo 5.1
Diseñe un programa que dados dos números enteros determine la suma y cual
de ellos es mayor, usando dos funciones diferentes.
#include <stdio.h>
#include <conio.h>
void suma (int a, int b); /*Declaraci¢n de la funci¢n*/
void mayor (int a, int b); /*Tipo de dato, nombre de la funci¢n y el tipo y nombre
de los argumentos*/
main()
{
int a, b;
printf("Ingrese el valor de a:\n");
scanf("%d", &a);
printf("Ingrese el valor de b:\n");
scanf("%d", &b);
suma(a,b); /*Llamado de la funci¢n*/
mayor(a,b); /*Unicamente el nombre de la funci¢n y de los par metros*/
getch();
return 0;
}
void suma(int a, int b) /*Definici¢n de la funci¢n*/
{ /*Abrimos llaves al inicio de la definici¢n*/
int sum; /*Declaraci¢n de las variables locales*/
sum=a+b;
printf("El valor de la suma es %d:\n\n", sum);
} /*Fin de la funci¢n suma*/
void mayor(int a, int b)
{
if(a==b)
printf("Son iguales\n\n");
else
{
if(a>b)
printf("El valor de a es mayor que el de b\n\n");
else
printf("El valor de b es mayor que el de a\n\n");
}
}

Definición de la Función

La función ha sido declarada, ha sido llamada y por lo tanto deber haber sido
definida. Lo cual consta de dos partes, las cuales son:

1. La Primera Línea

Que como su nombre lo indica, es la primera línea de la definición de la función


y con ella le indicamos al compilador que está en presencia de una función. Su
formato es el siguiente:

Tipo_de_dato nombre_de_la_función (tipo y nombre de los argumentos)

2. Cuerpo de la función
Se inicia con una llave “{“, y en ella, se pueden realizar asignaciones, cálculos,
impresiones, así como la declaración de las variables locales. Puede estar
constituidas por estructuras secuenciales, selectivas, iterativas, anidamientos, se
pueden llamar otras funciones, etc; finaliza con “}”. Puede devolver uno o ningún
valor.

Ejemplo 5.2
Diseñe un Programa en C, que Dado un número entero y mayor que cero,
Determine si es o no un número Primo. Ojo, los números primos sólo son
divisibles por el mismo y por la unidad (1).
#include <stdio.h>
#include <conio.h>
void primo (int numero);
main()
{
int numero, ban=1;
clrscr();
while(ban==1)
{
printf("Introduzca el n£mero por favor:\n");
scanf("%d", &numero);
while(numero<0)
{
printf("ERROR, el valor del n£mero debe ser mayor que cero\n");
scanf("%d", &numero);
}

primo(numero);

printf("¨Otro n£mero (si=1 y No=0)?\n");


scanf("%d", &ban);
}
getch();
return 0;
}
void primo (int numero)
{
int div, primo=1;
for(div=2; div<numero; div++)
{
if(numero%div==0)
{
primo=0;
printf("%d NO es primo\n\n\n", numero);
return 0;
}
else
primo=1;
}
if(primo!=0)
printf("%d es primo\n\n\n", numero);
}

2. Funciones que devuelven un valor entero


Las funciones que devuelven algún valor, se les llama PROTOTIPOS DE
FUNCIONES:
Antes de usar una función C debe tener conocimiento acerca del tipo de dato que
regresara y el tipo de los
parámetros que la función espera.
El estándar ANSI de C introdujo una nueva (mejor) forma de hacer lo anterior
respecto a las versiones previas
de C.
La importancia de usar prototipos de funciones es la siguiente:
Se hace el código mas estructurado y por lo tanto, más fácil de leer.
Se permite al compilador de C revisar la sintaxis de las funciones llamadas.
Lo anterior es hecho, dependiendo del alcance de la función. Básicamente si una
función ha sido definida antes de que sea usada (o llamada), entonces se puede
usar la función sin problemas.
Si no es así, entonces la función se debe declarar. La declaración simplemente
maneja el tipo de dato que la función regresa y el tipo de parámetros usados por
la función.
Es una práctica usual y conveniente escribir el prototipo de todas las funciones
al principio del programa, sin embargo esto no es estrictamente necesario.
Para declarar un prototipo de una función se indicara el tipo de dato que
regresará la función, el nombre de la función y entre paréntesis la lista del tipo
de los parámetros de acuerdo al orden que aparecen en la definición de la
función. Por ejemplo:
int longcad(int n); Lo anterior declara una función llamada longcad que regresa
un valor entero y acepta otro valor entero como parámetro.
Ejemplo 5.3
Diseñe un programa, que dado un número entero y mayor que cero, muestre su
factorial. (El factorial de 5 es 120; 5x4x3x2x1=120)
#include <stdio.h>
#include <conio.h>
int factorial (int num);
main()
{
int num, ban=1;
clrscr();
while(ban==1)
{
printf("Ingrese el valor del n£mero por favor:\n");
scanf("%d", &num);
while(num<0)
{
printf("ERROR, el valor del n£mero debe ser mayor que
cero:\n");
scanf("%d", &num);
}
printf("El valor del factorial es %d\n\n", factorial (num));
printf("¨Desea Realizar otro calculo?Si=1 y No=0\n");
scanf("%d", &ban);
}
getch();
return 0;
}
int factorial (int num)
{
int sum=1, i;
for(i=2; i<=num; i++)
{
sum=sum*i;
}
return (sum);
}

Explicación:
Quizá, lo único nuevo, e importante de explicar, radica en la llamada y la
definición de la función. Cuando una función nos devolverá un valor entero, al
identificador de dicha función debe precederle el tipo de dato. En el lugar, donde
llamamos la función, es que aparecerá el valor que nos devuelva, como valor de
retorno. En nuestro ejemplo, en una impresión. Y al momento de definirla, no se
nos debe olvidar, colocarle la sentencia return(); ya que, mediante esta
declaratoria, está retornando el valor calculado.
Pero, que sucede cuando se está trabajando, con valores bastante grandes, al
utilizar solamente el int, se producirá un error lógico; ya que como valor de
retorno podría ser un cero o una cifra negativa. Por tanto debemos usar el tipo
de dato “long int”.

Ejemplo 5.4
Diseñe un programa, que dada una cifra entera y mayor que cero, sea elevada a
una potencia introducida por el usuario, la cual. (Ejemplo: 5^2=25).
#include <stdio.h>
#include <conio.h>
long int potencia (int base, int exponente);
main()
{
int base, exponente;
clrscr();
printf("La Base es:\n");
scanf("%d", &base);
while (base<0)
{
printf("ERROR, el dato debe ser mayor que cero:\n");
scanf("%d", &base);
}
printf("El Exponente es:\n");
scanf("%d", &exponente);
printf("%d ^ %d es %ld\n\n", base, exponente, potencia(base,exponente));
getch();
return 0;
}
long int potencia (int base, int exponente)
{
long int sum=0, i,x;
for(i=1; i<exponente; i++)
{
x=base*base;
sum=sum+x;
}
return (sum);
}

Este método es un poco complejo y puede realizarse de manera más fácil,


haciendo uso de las funciones predefinidas en C, de las cuales hablaremos a
continuación.

Vous aimerez peut-être aussi