Arreglos Los arreglos o arrays son colecciones de

Anuncio
Arreglos
Los arreglos o arrays son colecciones de datos del mismo tipo. De esta manera, podemos
encontrar arrays de números enteros, arrays de números double, arrays de caracteres.
Tradicionalmente, la representación gráfica de un array es la siguiente:
0
1
2
3
4
Donde en cada posición del array se puede guardar un elemento.
Formalmente, un array se declara de la siguiente manera:
tipo_dato nombre_array[cant_elem];
donde,
• tipo_dato: indica qué tipo de dato va a contener el array
• nombre_array: indica cómo vamos a nombrar al array
• [cant_elem]: indica la cantidad máxima de elementos que el array puede
contener
La cantidad máxima de elementos que un array puede contener, es decir, su tamaño, se
define cuando se lo declara, luego no puede ser modificada. Esto se debe a que el
tamaño del array define la cantidad de espacios contiguos de memoria que la máquina
debe reservar para sostenerlo.
Se dice que los arrays permiten acceso directo a los datos. Esto es porque, mediante el
uso de un subíndice, uno puede acceder directamente a cada posición.
Por ejemplo:
numeros
4
3
-8
5
5
0
1
2
3
4
Acá tenemos un array al que vamos a llamar numeros. Este array tiene 5 posiciones, cada
una conteniendo un número entero. Entonces, la declaración sería:
int numeros[5];
Para obtener cualquier número contenido en él, sólo es necesario especificar la posición
en la que se encuentra. Por ejemplo:
• para obtener el -8 -> numeros[2]
• para obtener el 4 -> numeros[0]
Algo muy importante a tener en cuenta: la primera posición en un array es la posición 0
(no la 1). Es por esto que al querer obtener el número 4 hacemos numeros[0]. Si en
cambio hiciéramos numeros[1] obtendríamos el número 3.
Otro detalle importante: a cada posición de un array se la puede tratar como una variable
individual, por lo que se la puede comparar con otra variable, asignarle un valor, etc.
Ya vimos como definir o declarar un array o vector. Ahora veamos cómo inicializarlo.
Hay varias formas de inicializar un vector.
Supongamos que tenemos un vector de 5 posiciones y queremos inicializar cada posición
en -1 (podría ser cualquier otro valor). Una alternativa sería la siguiente:
int i;
// declaramos el array
int numeros[5];
// inicializamos el array
for (i = 0; i < 5; i++)
{
numeros[i] = ­1;
}
// mostramos el array inicializado
for (i = 0; i < 5; i++)
{
printf("numeros[%d] = %d\n", i, numeros[i]);
}
Con el primer ciclo for inicializamos cada posición del array en -1. Luego, con el segundo
for podemos recorrer el array y ver qué tiene cada posición, en este caso, el número -1.
Una cosa a notar: para acceder a cada posición usamos de subíndice el iterador i. De
esta manera logramos que en la primera iteración del ciclo accedamos a numeros[0], en
la segunda a numeros[1] y así sucesivamente hasta llegar a numeros[4].
Otra forma de inicializar este array sería especificando un listado, separado por comas y
encerrado entre llaves, de "inicializadores":
int i;
// declaramos e inicializamos el array
int numeros[5] = {­1, ­1, ­1, ­1, ­1};
// mostramos el array inicializado
for (i = 0; i < 5; i++)
{
printf("numeros[%d] = %d\n", i, numeros[i]);
}
Con este método hay que tener cuidado porque:
• si en el listado aparecen menos inicializadores que la cantidad máxima de
elementos del array, los que falten serán inicializados automáticamente en 0.
• si en el listado aparecen más inicializadores de los necesarios, ocurrirá un error de
sintaxis.
Por último, podríamos declarar e inicializar el array así:
int numeros[] = {­1, ­1, ­1, ­1, ­1};
De esta manera, el array se creará con tantas posiciones como inicializadores haya entre
llaves.
Demás está decir que los valores del array se pueden cargar a partir de valores
ingresados por teclado. Por ejemplo:
int i;
int numeros[5];
// solicitamos al usuario cargar los numeros
for (i = 0; i < 5; i++)
{
printf("Ingrese un numero entero: ");
scanf("%d", &numeros[i]);
}
// mostramos el array cargado
for (i = 0; i < 5; i++)
{
printf("numeros[%d] = %d\n", i, numeros[i]);
}
Acá el usuario es el que carga los valores en el array. Nótese el uso del scanf(): cada
número ingresado va a ser guardado en una posición distinta del array numeros, ya que i va variando entre 0 y 4.
La utilidad principal de un array está dada en la definición misma: agrupa datos del mismo
tipo. Antes si queríamos trabajar con cinco números enteros debíamos declarar cinco
variables de tipo int. Utilizando el concepto de array podemos declarar sólo una variable:
un array de enteros con cinco posiciones: int array[5].
Operaciones sobre un array
Teniendo cargado un array, se pueden realizar múltiples operaciones, como ser buscar un
elemento determinado del array, buscar mínimos y máximos, u ordenar los elementos.
Buscar un elemento es sencillo. Simplemente es ir recorriendo el array y comparando
cada elemento con el valor buscado.
Considerando que el array ya se encuentra cargado con valores válidos y que la máxima
cantidad de elementos que puede almacenar está dada por la constante TAM_ARRAY,
podemos plantear una función de búsqueda, que reciba como parámetros el array y el
elemento buscado, y que devuelva la posición de dicho elemento.
El prototipo de la función sería:
int buscar_elemento(int [], int);
Es decir, una función de nombre buscar_elemento, que recibe como parámetros un
array de enteros y un entero, y devuelve un entero.
La definición de la función quedaría así:
int buscar_elemento(int numeros[], int buscado)
{
int i;
for (i = 0; i < TAM_ARRAY; i++)
{
if (numeros[i] == buscado)
{
return i;
}
}
return ­1;
}
Esta función recorre el array pasado como parámetro y compara cada elemento con el
buscado. En el momento que lo encuentra, devuelve la posición de dicho elemento ( i). Si
termina de recorrer el array sin encontrar el elemento buscado, devuelve ­1, que tendrá
que ser interpretado apropiadamente por la función que llamó a buscar_elemento.
En general, cuando le pasamos por parámetro una variable a una función, internamente lo
que el programa hace es: hacer una copia del valor de la variable y pasarle a la función
esa copia (no la variable original). ¿Esto qué trae como consecuencia? Que por más que
pasemos variables a una función como parámetros, éstas (las variables originales) no se
van a modificar. O sea, cualquier cambio que sufran dentro de la función, se perderá en
cuanto la función retorne. Esto se llama paso de parámetros por valor. Formalmente, se
pasa como parámetro el valor de la variable, no la variable en sí.
Con los arrays la cosa funciona distinto. Un array se pasa por referencia. Es decir, la
función que recibe el array como parámetro, recibe en realidad la dirección de memoria en
donde comienza el array. De esta forma tiene acceso al array original y no a una copia del
mismo. Entonces, cualquier modificación que sufra el array dentro de la función, no se
perderá.
Un ejemplo de este comportamiento lo podemos ver planteando una función para ordenar
un array. La función recibirá un array como parámetro, que será ordenado. Como método
de ordenamiento vamos a usar el algoritmo de bubble sort o burbujeo.
El prototipo de la función sería el siguiente:
void ordenar(int []);
Nótese que la función no retorna nada. Esto es porque el resultado será el mismo array
que recibe como parámetro, pero ordenado. Es decir, la función modificará el array que
recibe, y esa modificación perdurará.
La definición de la función sería la siguiente:
void ordenar(int numeros[])
{
int i, j;
int aux;
for (i = 1; i < TAM_ARRAY; i++)
{
for (j = 0; j < TAM_ARRAY ­ 1; j++)
{
if (numeros[j] > numeros[j+1])
{
aux = numeros[j];
numeros[j] = numeros[j+1];
numeros[j+1] = aux;
}
}
}
}
Este método se llama burbujeo, porque a medida que se va recorriendo el array los
valores más pequeños van subiendo posiciones, como las burbujas sumergidas suben
hasta la superficie.
Por cada "pasada", representada por i, cada valor se va ubicando en la posición que le
corresponde: en la primera, el más chico se ubicará en numeros[0], en la segunda, el
segundo más chico se ubicará en numeros[1] y así sucesivamente hasta que hayamos
pasado por todo el array.
El programa completo sería el siguiente:
#include <stdio.h>
#define TAM_ARRAY 5
void ordenar(int []);
int main()
{
int i;
// declaramos e inicializamos el array
int numeros[TAM_ARRAY] = {3, 9, 7, 1, 4};
// mostramos el array cargado
for (i = 0; i < TAM_ARRAY; i++)
{
printf("numeros[%d] = %d\n", i, numeros[i]);
}
// ordenamos el array
ordenar(numeros);
printf("\nArray ordenado:\n");
// mostramos el array ordenado
for (i = 0; i < TAM_ARRAY; i++)
{
printf("numeros[%d] = %d\n", i, numeros[i]);
}
return 0;
}
void ordenar(int numeros[])
{
int i, j;
int aux;
for (i = 1; i < TAM_ARRAY; i++)
{
for (j = 0; j < TAM_ARRAY ­ 1; j++)
{
if (numeros[j] > numeros[j+1])
{
aux = numeros[j];
numeros[j] = numeros[j+1];
numeros[j+1] = aux;
}
}
}
}
Nótese cómo la función trabaja sobre el array original. La función en ningún momento
retorna el array numeros modificado, y sin embargo, cuando lo recorremos por segunda
vez en el main podemos ver los efectos de la función ya que está ordenado.
Arrays de caracteres
Los arrays de caracteres son un caso particular de arrays, con los que podemos lograr
expresar cadenas o strings.
En C, una cadena se define como un array de caracteres terminado con el caracter nulo
('\0').
Esto implica que cuando se reserva memoria para una cadena, hay que tener en cuenta
una posición más para el caracter nulo.
Por ejemplo: char cadena[] = "hola" no ocupa 4 posiciones de memoria, sino 5.
El caracter nulo es importantísimo, ya que le indica a la computadora cuándo termina la
cadena. ¿Y esto para qué sirve? Para hacer cosas como esta:
printf("%s", cadena);
De esta manera podemos mostrar el contenido de cadena, sin necesidad de tener que
hacer esto:
for (i = 0; i < 5; i++)
{
printf("%c", cadena[i]);
}
Planteemos entonces un ejercicio sencillo:
"Escribir una función que permita ingresar un texto de hasta 1000 caracteres, finalizando
la carga con un ENTER. Aplicarla a un programa que se encargue de solicitar el ingreso
del texto, y muestre luego el texto ingresado."
La función se va a encargar de:
– tomar uno a uno los caracteres ingresados por teclado, hasta que se ingrese un
ENTER ('\r') e ir guardándolo en un array.
– una vez terminado el ingreso, agregar un '\0' en la posición siguiente al último
caracter guardado en el array mencionado, para indicar el final del mismo.
El código de la función sería el siguiente:
void ingresar_texto(char texto[])
{
char letra;
int i = 0;
printf("Ingrese el texto: ");
letra = getche();
while ((letra != '\r') && (i < MAX_TEXTO ­ 1))
{
texto[i] = letra;
i++;
letra = getche();
}
texto[i] = '\0';
}
La variable i indica en qué posición se va a guardar la letra ingresada. Una vez que
termina el while (ya sea porque se ingresó ENTER o la cantidad máxima de caracteres) va
a indicar la posición siguiente al último caracter válido ingresado.
El programa completo quedaría así:
#include <stdio.h>
#include <conio.h>
#define MAX_TEXTO 1000
void ingresar_texto(char []);
int main()
{
char texto[MAX_TEXTO];
ingresar_texto(texto);
printf("\n%s", texto);
return 0;
}
void ingresar_texto(char texto[])
{
char letra;
int i = 0;
printf("Ingrese el texto: ");
letra = getche();
while ((letra != '\r') && (i < MAX_TEXTO ­ 1))
{
texto[i] = letra;
i++;
letra = getche();
}
texto[i] = '\0';
}
Partiendo de esta base, las cadenas se pueden manipular como cualquier otro array,
buscando caracteres, reemplazándolos, etc.
Algo interesante que se puede hacer específicamente con cadenas es, por ejemplo,
contar cuántas palabras hay en un texto ingresado. Esto lo podemos hacer teniendo en
cuenta los separadores más comunmente usados ( ' ', '.', ','. ':', ';', '¿', '?',
'¡', '!' por mencionar algunos).
Las funciones serían las siguientes:
int contar_palabras(char texto[])
{
int i = 0, cont_palabras = 0;
int forma_palabra;
while (texto[i] != '\0')
{
// descartar separadores antes de la primera palabra
while (es_separador(texto[i]))
{
i++;
}
forma_palabra = 0;
// buscar el final de cada palabra
while (!es_separador(texto[i]) && texto[i] != '\0')
{
forma_palabra = 1;
i++;
}
// contar la palabra encontrada
if (forma_palabra)
{
cont_palabras++;
}
}
return cont_palabras;
}
int es_separador(char c)
{
if (c == ' ' || c == ',' || c == '.' || c == ':' || c == ';' || c == '¿' || c == '?' || c == '¡' || c == '!')
{
return 1;
}
return 0;
}
La función es_separador recibe un caracter y se fija si es un separador conocido. De ser
separador, retorna 1 (VERDADERO), si no, retorna 0 (FALSO).
La función contar_palabras primero descarta todos los separadores que puede llegar a
haber antes de la primera palabra (el texto podría empezar con un signo de admiración
por ejemplo).
Luego comienza a contar carateres que no sean separadores, hasta encontrar un
sepador: así encontramos una palabra. Cada vez que encuentra una palabra, la cuenta
con cont_palabras. La variable bandera forma_palabra sirve para indicar que se están
contando los caracteres de una palabra, es decir, estamos en el medio de una palabra,
procesándola.
Descargar