Vectores,matrices y estructuras

Anuncio
Cristian Blanco www.cristianblanco.es
Vectores y matrices ; Pagina :1 de 11
TEMA 3(b).-Estructuras de datos
VECTORES , MATRICES y ESTRUCTURAS
 Los vectores son datos del mismo tipo agrupados bajo un mismo nombre y accesibles o
referenciados de forma individual mediante un índice, que es un número entero que va de
cero en adelante
 El mayor valor que puede tomar el índice se denomina dimensión del vector
 La definición de vectores en C se realiza del sgte. modo:
Tipo de dato nombre[dimensión];
Así si queremos definir un vector de 100 enteros pondremos:
int v[100];
donde los elementos se almacenan en v[0]…v[99];
 La dimensión suele ponerse antes del main() con las declaraciones:
#define dim 100 ó también const int dim=100; si queremos tamaño 100.
De este modo los programas son fácilmente parametrizables pues cambiando el valor de dim
trabajamos con nueva dimensión a lo largo de todo el programa.
 Así se definen los vectores estáticos que reservan memoria en tiempo de compilación
 Los datos son reservados en posiciones de memoria contiguas, al igual que las cadenas. De
hecho estas últimas son un caso particular de vectores.
 No existen funciones de librería estándar para manejar los vectores en bloque, salvo bsearch
y qsort, que se verán si son necesarias.
 La lectura de datos se realiza uno a uno en bucles tipo for…..
 Al igual que en caracteres el nombre de un vector es un puntero a la dirección de memoria
donde empieza dicho vector, es decir, es lo mismo v que &v[0]. De todos modos es mejor
manejar los vectores con sus índices que con sus punteros a memoria, siempre que esto pueda
hacerse..
 Debido a la relación entre nombres de vectores y punteros la declaración de parámetros en
funciones cuando éstos son vectores puede realizarse de dos formas:
a) void funcion(int v[]……)
b) void funcion(int *v…….)
Los vectores son siempre parámetros por referencia, cosa que el compilador realiza para mayor
eficiencia en el paso de parámetros.
Lo que se pasa es la dirección del primer elemento, es cuestión del programador no salirse de la
dimensión, para no provocar un “machacamiento” de la memoria, al igual que ocurre en
caracteres.
Es preferible la primera forma a), pues se indica con los corchetes que se pasa un vector.
Cristian Blanco www.cristianblanco.es
Vectores y matrices ; Pagina :2 de 11
 Es muy útil cuando realizamos programas con vectores numéricos el utilizar la función
random() para llenarlos de valores y evitarnos así la penosa tarea de introducir un conjunto
grande de datos, sobre todo cuando queremos manejar grandes vectores.
Ejemplo:
// esta secuencia de programa genera un vector de 500 enteros, con números entre 1 y 100
for(t=0;t<500;t++)
v[t]=random(100)+1;
 Los vectores pueden ser inicializados de forma explícita enumerando sus datos, esto es :
int x[5]={1,2,3,4,5};
Si en la lista de valores existen menos datos de los que marca la dimensión el resto son
inicializados a cero.
MATRICES
 Una matriz puede considerarse como el apilamiento o agrupamiento de vectores, por tanto
cada elemento individual debe ser referenciado con dos índices uno de fila y otro de
columna. Puede representarse gráficamente como una tabla donde el índice que varía en
vertical es el de fila y el horizontal el de columna.
Fila 1
Fila 2
Fila 3
Columna 1
Dato
Dato
Dato
Columna 2
dato
dato
dato
Columna 3
dato
dato
dato
El esquema anterior representa una matriz de 3 filas por 3 columnas.
 El estándar ANSI C permite generalizar matrices de hasta 12 índices
 Es decir sería valido definir una matriz de 3x5x2
 Como se definen en C las matrices: tipo nombre[dimension1][dimension2]
Ejemplo:
float x[20][30]; define una matriz de 20 filas por 30 columnas.
Los elementos van desde el x[0][0]….. x[19][29];
 Para la lectura ó asignación de matrices se utilizan con frecuencia bucles encajados ó
anidados esto es:
int x[20][30];
for(i=0;i<20;i++)
for(k=0;k<30;k++)
x[i][k]=100;
Esta secuencia inicializa la matriz x de 20x30 con todos sus elementos a valor 100;
Si se leyeran por teclado esto seria del siguiente modo:
Cristian Blanco www.cristianblanco.es
Vectores y matrices ; Pagina :3 de 11
int x[20][30];
for(i=0;i<20;i++)
for(k=0;k<30;k++)
{
printf(“dame datos para el elemento %d / %d”,i,k);
scanf(“%d”,&x[i][k]);
}
El printf es muy recomendable ponerlo para guiar al usuario por donde vamos
 Lo dicho con anterioridad para variables individuales sobre locales, globales ó estáticas es
aplicable igualmente para vectores y matrices.
 Las matrices son pasadas igualmente por referencia a las funciones cuando son parámetros.
 Para indicar que se pasan matrices a una función tanto en el prototipo (antes del main), como
en la cabecera de su cuerpo se pone función (int a[][5]….) , la segunda dimensión es
obligada, por motivos de localización en memoria de los elementos, téngase en cuenta que el
acceso a los elementos se realiza mediante la formula direccion_base+num de elementos por
fila(segundo índice)*tamaño de cada elemento(según tipo)+columna, siendo necesario pues
especificar la segunda dimensión para saber el número de elementos por fila.
 Un caso particular de matrices son los vectores de caracteres, es decir, por ejemplo
char v[20][50], sería un vector de 20 elementos y cada elemento una cadena de 50 caracteres.
 Una matriz puede también ser inicializada de forma explícita como un vector, pero hay que
usar doble llave para separar las filas.
Ejemplo:
int x[2][2]={{1,2}, {5,7}}, esto produce la siguiente asignación
Primera fila
x[0][0]=1
x[0][1]=2
Segunda fila
x[1][0]=5
x[1][1]=7
Al igual que en vectores, si no existen suficientes valores los restantes de la matriz son puestos a
cero.
Cristian Blanco www.cristianblanco.es
Vectores y matrices ; Pagina :4 de 11
Estructuras.
 Una estructura es un tipo agregado de datos de los tipos básicos o de otros datos
estructurados que ya conocemos, agrupados bajo un mismo nombre.
 Una estructura en C la creamos del siguiente modo:
struct nombre_estructura
{
tipo_dato1 nombre1;
tipo_dato2 nombre2;
tipo_dato3 nombre3;
};
Con esto definimos los componentes de la estructura pero no se ha declarado ninguna variable,
esto es, sólo con lo anterior el compilador no reserva memoria alguna para estructuras de este
tipo, para ello necesitamos hacer además esto:
struct nombre_estrcutura variable; // con esto definimos variable como del tipo
nombre_estructura.
Ejemplo:
struct ficha
{
char desc[30];
long exist;
int recargo;
};
struct ficha almacen;
// con esto definimos una variable almacen que es una estructura.
Es decir la primera declaración no crea variables, crea el tipo y sus componentes y la segunda
crea las variables en memoria, podríamos hacer también esto:
struct ficha almac1,almac2; // creamos dos variables de estructura llamadas almac1 y almac2;
 ¿Cómo acceder a los miembros de una estructura? Una vez definida la estructura, para operar
con un miembro individual de la misma se hace mediante el operador. (punto)
En el ejemplo anterior para imprimir el dato recargo haríamos lo siguiente:
Printf("%d",almacen.recargo); es decir se accede a una estructura poniendo el nombre de la
variable de tipo estructura, punto y el nombre del elemento
Cristian Blanco www.cristianblanco.es
Vectores y matrices ; Pagina :5 de 11
 Otra forma muy cómoda de definir estructuras es utilizar typedef, esta declaración usada en
estructuras crea un "alias " de la estructura de forma que el nombre definido con typedef es
un nuevo tipo de datos:
Ejemplo:
typedef struct
{
char desc[30];
long exist;
int recargo;
} tipo_ficha;
/* hemos definido un alias de struct ficha llamado tipo_ficha que ahora es como si fuese un tipo
int, long,float etc. y este nombre puede ser usado para definir estructuras de este tipo.*/
tipo_ficha var1,var2; //hemos definido dos variables tipo estructura llamadas var1 y var2
.....
....
 Tipos que pueden ser definidos dentro de una estructura:





Tipos fundamentales (int,long,char,float etc..)
Tipo vector o matriz (incluidas cadenas de caracteres)
Punteros.
Estructuras.
También uniones y funciones, pero el uso de ellas no serán materia de este curso.
 Como estructuras dentro de otras, por ejemplo:
struct fecha
{
int dia,mes,anyo;
};
struct ficha
{
char nombre[25];
char apellido[30];
struct fecha fecha_nac;
}
( aquí el elemento fecha_nac se crea con referencia a la estructura fecha que debe ser
declarado antes)
struct ficha persona; // aqui se declara la variable en memoria que será persona
si queremos acceder al dato anyo de su fecha de nacimiento pondremos
persona.fecha_nac.anyo, es decir la componente fecha_nac y dentro de ella especificamos el año.
Cristian Blanco www.cristianblanco.es
Vectores y matrices ; Pagina :6 de 11
 Vectores de estructuras. Podemos y nos será muy útil para el caso de aplicar algoritmos de
búsqueda y ordenación el crear vectores de estructuras, esto se hace de forma muy sencilla ,
viendo el siguiente ejemplo:
typedef struct
{
char nombre[20];
char apellido[30];
int edad;
}tipo_persona;
tipo_persona alumnos[100];
se ha declarado un vector de 100 celdas donde cada una es una estructura, sería legal hacer
referencia por tanto a:
alumnos[k].edad // seria el dato edad del alumno en posición k+1 del vector.
 Operaciones con estructuras.
Básicamente podemos indicar con una variable de tipo estructura las siguientes operaciones:
Inicializarla en el momento de definirla:
struct ficha empleado={"Santos Lopez","Francisco",25};
Obviamente pueden ser asignadas desde teclado con scanf y gets o mediante una función random
si no queremos teclear datos.
Acceder a sus miembros mediante el operador punto(.) poniendo nombre de estructura y
nombre del dato componente.
Asignar una estructura a otra con el operador de igualdad, esto debe hacerse teniendo cuidado
de que ambas sea de la misma configuración de datos o podríamos tener problemas de machacar
la memoria. Es decir:
// si per1 y per2 son estructuras de tipo tipo_persona se puede hacer
per1=per2;
// y copiará todos los datos de per2 encima de per1;
 Paso de estructuras a funciones
Si trabajamos con funciones donde los parámetros son estructuras éstas pueden pasarse por valor
o referencia, y si queremos pasar por referencia una estructura a una función, por ser parámetro
por referencia debemos hacer como siempre la indicación de que se pasa un puntero, por
ejemplo:
Cristian Blanco www.cristianblanco.es
Vectores y matrices ; Pagina :7 de 11
#include <stdio.h>
#include <ctype.h>
#include <conio.h>
struct ficha
{
char nombre[15];
int edad;
int codigo;
};
void funcion(struct ficha *pficha);
main()
{
struct ficha midato;
int t;
printf("dame el nombre:");
flushall();
scanf("%s",midato.nombre);
printf("dame la edad:");
scanf("%d",&midato.edad);
printf("dame el codigo:");
scanf("%d",&midato.codigo);
funcion(&midato);
printf("valor modificado: %d",midato.edad);
}
void funcion(ficha *pficha)
{
pficha->edad=pficha->edad+1;
}
Fijarse que pficha es un puntero a ficha, y cuando luego usamos dentro de la función un dato
de pficha lo indicamos poniendo pficha->edad
Cristian Blanco www.cristianblanco.es
Vectores y matrices ; Pagina :8 de 11
Creación de tablas dinámicas
Es muy común que cuando necesitamos usar un vector o matriz no sepamos las dimensiones, en
el momento del diseño del programa, para ello en C tenemos unas funciones que nos permiten
reservar memoria para tipos de datos, en particular vectores y matrices, pero en tiempo de
ejecución es decir el programa obtiene el tamaño desde el exterior (bien por los usuarios o desde
un fichero) y a continuación reserva memoria para los vectores y matrices necesarios.
Las dos funciones que vamos a ver son malloc y calloc y ambas están en el fichero de cabecera
<stdlib.h> el cual habrá que referenciar para utilizar dichas funciones, ambas funciones son
ANSI C.
 Función malloc
La función malloc reserva memoria dinámicamente del montículo (heap) de memoria disponible
por el compilador, tiene un parámetro que es el numero de bytes que se desean reservar y
devuelve un puntero al bloque de memoria reservada y para el tipo de datos que se especifique
(char, int, float etc. etc) Si no puede reservar la memoria referida por falta de la misma devuelve
un puntero NULL.
Su uso es el siguiente:
int *pa=NULL; // puntero a un entero
pa=(int *)malloc(1000*sizeof(int));
// reserva 4000 bytes de memoria , para enteros que comienzan en pa
/* fijarse que utilizo el operador sizeof(tipo) que me da la longitud en bytes de un entero
cosa que puede variar segun la arquitectura de la máquina*/
pa[2]=25;//asigno la celda 3 con un 25
........
.......
// podria manejarse desde pa[0]....pa[999] sin problemas pues ha sido reservados
// 1000 elementos
El siguiente programa ilustra completamente el uso de malloc
/*************** Array dinámico de una dimensión ***************/
/* arrdim01.c
*/
#include <stdio.h>
#include <stdlib.h>
main()
{
int *pa = NULL;
unsigned int nbytes = 0, nElementos=0, i = 0;
printf("Número de elementos del array: ");
scanf("%u", &nElementos);
fflush(stdin);
Cristian Blanco www.cristianblanco.es
Vectores y matrices ; Pagina :9 de 11
nbytes = nElementos * sizeof(int);
if ((pa = (int *)malloc(nbytes)) = = NULL )
{
printf("Insuficiente espacio de memoria\n");
exit(-1);
}
printf("Se han asignado %u bytes de memoria\n", nbytes);
// pa es el vector listo para usarse
/* Inicializar el array */
for ( i = 0; i < nElementos; i++ )
pa[i] = random(500)+1;
/* imprimir el vector generado */
for ( i = 0; i < nElementos; i++ )
printf("%d--",pa[i]);
/* Operaciones */
free(pa);
}
Fijarse que al final del programa se usa otra función
 free (puntero a bloque de memoria reservada) , esta función es la inversa de malloc y libera la
memoria reservada para que pueda ser usada por el resto del programa más adelante. El
parámetro es el puntero de memoria que se reservó para la tabla. Si la memoria no se libera,
al salir al sistema operativo se libera automáticamente por supuesto.
 Función calloc
Esta función se comporta de igual modo que malloc solo que sus parámetros son el número de
elementos de la tabla y el tamaño de cada uno con lo que la forma valida de usarla sería:
pa=(int*)calloc(1000,sizeof(int));
Es decir reservar memoria para 1000 datos de tamaño un entero, la función devuelve igual que
antes un puntero al bloque de memoria asignado o si no un puntero a NULL.
Cristian Blanco www.cristianblanco.es
Vectores y matrices ; Pagina :10 de 11
 Matrices dinámicas
Lo dicho para vectores, puede hacerse también para matrices pero es algo más costoso de
escribir. Se trata de hacer un vector de punteros que apunten al comienzo de cada fila, que será
también un puntero, aquí pues aparece el concepto de puntero a puntero. El siguiente programa
ilustra el uso de matrices dinámicas creadas con malloc:
Graficamente seria esto:
ppa
/************** Array dinámico de dos dimensiones **************/
/* arrdim02.c
*/
#include <stdio.h>
#include <stdlib.h>
void main()
{
// se define ppa como puntero a puntero a entero
int **ppa = NULL;
unsigned int nFilas = 0, nCols = 0;
unsigned int f = 0, c = 0;
printf("Número de filas del array:
scanf("%u", &nFilas);
fflush(stdin);
");
printf("Número de columnas del array: ");
scanf("%u", &nCols);
fflush(stdin);
/* Asignar memoria para el array de punteros */
if ((ppa = (int **)malloc(nFilas * sizeof(int *))) == NULL)
{
printf("Insuficiente espacio de memoria\n");
exit(-1);
}
Cristian Blanco www.cristianblanco.es
Vectores y matrices ; Pagina :11 de 11
/* Asignar memoria para cada una de las filas */
for (f = 0; f < nFilas; f++)
{
if ((ppa[f] = (int *)malloc(nCols * sizeof(int))) == NULL)
{
printf("Insuficiente espacio de memoria\n");
exit(-1);
}
}
/* Inicializar el array a cero */
for ( f = 0; f < nFilas; f++ )
for ( c = 0; c < nCols; c++ )
ppa[f][c] = 0;
/* Operaciones */
for ( f = 0; f < nFilas; f++ )
{
for ( c = 0; c < nCols; c++ )
printf("%d ", ppa[f][c]);
printf("\n");
}
/* Liberar la memoria asignada a cada una de las filas */
for ( f = 0; f < nFilas; f++ )
free(ppa[f]);
/* Liberar la memoria asignada al array de punteros */
free(ppa);
}
 Se deben usar vectores y/o matrices dinámicas sólo cuando sea necesario. En aquellos
programas donde sepamos el tamaño de las estructuras de datos es más cómodo trabajar con
dimensiones fijas marcada por #define o const. Si el tamaño no se sabe o no se da como dato
estimado en el problema, entonces sí que habrá que reservar el espacio sobre la marcha.
Descargar