TEMA 10 DATOS ESTRUCTURADOS 10.1. Matrices 10.2. Strings

Anuncio
TEMA 10
DATOS ESTRUCTURADOS
10.1. Matrices
10.2. Strings
10.3. Estructuras
10.4. Enumerados
10.5. Punteros
10.1. Matrices
Una matriz es una colección de variables del
mismo tipo, denominadas por un nombre común
int var0, var1, var2;
EQUIVALENTE A
int vars[3];
A cada elemento especı́fico del array se accede
con un ı́ndice
int var0, var1, var2;
EQUIVALENTE A
int vars[3];
Ahora podemos tratar múltiples datos de una
manera más sencilla
for (i=0; i<3; i++) vars[i] = (i+1)*2;
Dimensiones de una matriz
• Una matriz puede tener una o varias
dimensiones
int una_dim[10];
char dos_dim[10][15], tres_dim[5][10][4];
Las matrices uni-dimensionales son llamadas
vectores
• Disposición de una matriz en memoria
int dos_dim[3][4];
[0][0]
···
[0][3]
[0][0]
[1][0]
[2][0]
[0][1]
[1][1]
[2][1]
[1][0]
[0][2]
[1][2]
[2][2]
···
[0][3]
[1][3]
[2][3]
[2][2]
[2][3]
Definición de una matriz
• Según el momento en el que se asigna el
tamaño:
– Arrays estáticos: Se definen en tiempo de
compilación
– Arrays dinámicos: Se definen en tiempo de
ejecución
• C no comprueba que los limites del array sean
desbordados
int i, vect[10];
for (i=0; i<10; i++) vect[i+1] = vect[i];
– Sobrepasar los lı́mites del array puede
implicar:
∗ Sobreescribir otras variables
∗ Corromper la estructura del programa
Ejemplo de uso
TABLA DE MULTIPLICAR
int i, j;
int mult[10][10];
for (i=0; i<10; i++)
for (j=0; j<10; j++)
mult[i][j] = i*j;
0
0
0
0
0
0
0
0
0
0
0
1
2
3
4
5
6
7
8
9
0
2
4
6
8
10
12
14
16
18
0
3
6
9
12
15
18
21
24
27
0
4
8
12
16
20
24
28
32
36
0
5
10
15
20
25
30
35
40
45
0
6
12
18
24
30
36
42
48
54
0
7
14
21
28
35
42
49
56
63
0
8
16
24
32
40
48
56
64
72
0
9
18
27
36
45
54
63
72
81
Inicialización de matrices
• C permite inicializar matrices en el momento
de su declaración
int i[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int i[2][3] = {0, 1, 2, 10, 11, 12};
char error[20] = "Fichero no encontrado.\n";
//longitud=24
• C permite no indicar el tamaño de una matriz
inicializada
int i[ ][3] = { 0, 1, 2, 10, 11, 12};
char error[ ] = "Fichero no encontrado.\n";
Paso de arrays a funciones
• Paso por variable (o valor)
– Definición de la función
float Mayor(float vector[120]) {...} // O:
float Mayor(float vector[]) {...}
– Llamada a la función EJEMPLO
float lista_notas[120], mejor_nota;
mejor_nota = Mayor (lista_notas);
• Paso por referencia (o puntero)
– Definición de la función
float Mayor(float
*vector) { ... }
– Llamada a la función EJEMPLO
float lista_notas[120], mejor_nota;
mejor_nota = Mayor (&lista_notas[0]);
10.2. Strings
• No existe el tipo cadena de caracteres. Estas
se implementan con vectores (acaban en un
carácter nulo \0)
’Esto es una constante de cadena de caracteres’
El carácter nulo está implı́cito y lo añade el
compilador
• Las funciones de tratamiento de cadenas se
encuentran en la librerı́a string
char *strcpy ( char *s1, const char *s2 );
//Copia s2 en s1
char *strcat ( char *s1, const char *s2 );
//Concatena s2 a s1
int strlen ( const char *s1 );
//Devuelve la longitud de s1
int strcmp ( const char *s1, const char *s2 );
//Compara alfabéticamente
Strings. Ejemplo
#include <stdio.h>
#include <string.h>
void main(void)
{
char c1[80], c2[80], c3[80];
gets(c1); //(get string c1)
gets(c2); //Lee una cadena y almacena en c2
printf("La 1a cadena mide %d.\n", strlen(c1));
printf("La 2a cadena mide %d.\n", strlen(c2));
if (strcmp(c1,c2)==0) printf("Y son iguales\n");
else printf("Pero son distintas cadenas\n");
strcpy(c3,c1); //Copia en c3 la 1a cadena leı́da
strcat(c3,c2); //Pega c2 detrás de c3
printf("Las cadenas leı́das forman %s\n",c3); (A)
printf("Las cadenas leı́das forman ");
(B)
puts(c3); printf("\n"); //Muestra una cadena (C)
//La sentencia (A) equivale a (B) + (C)
}
10.3. Estructuras
• Una estructura es una agrupación de variables
bajo un nombre común. Permite mantener
junta información relacionada
• Declaración de la estructura: Los campos se
declaran dentro de la estructura de la forma
habitual. Las variables pueden declararse tras
la declaración de la estructura:
struct lector
{ char nombre[80],
telefono[20],
direccion[100];
int codigo,
edad,
prestamos;} Rafa, Maria;
struct libro
{ char titulo[80],
Autor[80],
editorial[100];
int paginas,
prestado_a;} libro1, libro2;
Estructuras. Utilización
. . . ó pueden declararse después:
struct lector Pepe, x, y, socios[1000];
//declara 1003 lectores
struct libro bib[10000]; // declara 10000 libros
• Una estructura completa se comporta como
un tipo atómico.
struct libro libro_perdido;
libro_perdido = bib[3156];
• Se accede a los campos con la notación punto
if (nuevo_socio.edad > 26)
printf("No puedo extender su carnet joven");
lector23.prestamos++;
Estructuras. Ejemplo de uso
void Prestar(struct lector lec, struct libro lib)
{if (lib.prestado)
printf("El libro no está disponible");
else if (lec.prestamos>=3)
printf("ya tiene demasiados libros");
else {lib.prestado = lec.codigo;
lec.prestamos++;}}
Prestar(Pepe,bib[211]);
//Gestiona un prestamo a Pepe
10.4. Enumerados
• Una enumeración es un conjunto de constantes enteras, {0, 1, 2, 3, 4, . . .}, con nombre que
especifica todos los valores que ese campo
puede tener
• Declaración de la enumeración. Las variables pueden declararse tras la declaración de
la enumeración:
enum estilo
{aventuras, amor, cficcion, historia}
cat1,cat2,z,t;//declara 4 estilos
• Declaración de las variables después:
enum estilo x,mis_gustos,y;//declara 3 estilos
Enumerados. Ejemplo
struct lector
{char nombre[80],
telefono[20],
direccion[100];
int codigo,
edad,
prestamos
enum estilo pref;}
struct libro
{char titulo[80],
Autor[80],
editorial[100];
int paginas,
prestado_a;
enum estilo tipo;}
Tipos definidos por el usuario
C permite definir explı́citamente un nuevo tipo de
dato con la instrucción typedef
typedef struct lector
{char nombre[80],
telefono[20],
direccion[100];
int codigo,
edad,
prestamos
enum estilo pref;}
lector lec1,lec2,lec3;//define 3 lectores
Ejemplo de utilización:
void Listado_personalizado(lector l)
{int lib;
for(lib=0; lib<total_libros; lib++)
if (bib[lib].tipo == l.pref)
printf("%s\n",bib[lib].titulo);}
Listado_personalizado(lec3);
10.5. Punteros
El valor de cada variable está almacenado en una
dirección determinada de la memoria
Un puntero es una variable cuyo contenido es la
dirección de alguna variable
Diremos que un puntero “apunta” a una variable
si su contenido es la dirección de esa variable
Normalmente, un puntero ocupa 4 bytes de memoria
Debe definirse en base al tipo de la variable a la
que apunta
Ejemplo:
int *puntero
puntero es (una variable que apuntará a la dirección de / un puntero a) una variable de tipo
entero
Operadores de Dirección e Indirección
El lenguaje C dispone del operador de dirección,
&, que permite obtener la dirección de la variable
sobre la que se aplica
También se dispone del operador de indirección,
*, que permite acceder al contenido de la zona
de memoria a la que apunta el puntero sobre el
cual aplicamos dicho operador
Ejemplo:
int i, j, *p;
// p es un puntero
p=&i;
// p apunta a la dirección de i
*p=10;
// i toma el valor 10
p=&j;
// p apunta a la dirección de j
*p=11;
// j toma el valor 11
Operaciones legales con punteros
Ni las constantes ni las expresiones tienen dirección (no se les puede aplicar &)
No se permiten las asignaciones directas entre
punteros que apuntan a distintos tipos de variables (existe un tipo indefinido de puntero , void,
que funcionan como comodı́n para las asignaciones independientemente de tipos).
Ejemplo:
int *p;
double *q;
void *r;
p=q;
// ilegal
r=q;
// legal
p=r;
// legal
Aritmética de los punteros I
Un puntero contiene información no solo de la dirección en memoria de la variable a al que apuntan sino también de su tipo.
Sobre los punteros podremos:
• Sumar?
• Restar?
• Multiplicar¿
• Dividir¿
? significa que las unidades son los bytes de memoria que ocupa el tipo de las variables a las que
apunta
Aritmética de los punteros II
Ejemplo:
int *p;
double *q;
p + +;
/* sumar 1 a p, implica aumentar
2 bytes en la dirección a la que apunta */
q – – ;
/* disminuir 1 a q, implica disminuir 4 bytes en la dirección a la que apunta
*/
¿ significa que es una multiplicación y división entre constantes que suelen ser tamaño de dimensiones de matrices o tamaños de estructuras, y
número de fila o de columna (se verá más adelante)
Relación Matrices ∼ Punteros
Sea el array ası́ definido:
int array [8] [5];
Los arrays se almacenan en memoria por filas:
Fórmula de direccionamiento de una matriz:
“La posición en memoria del elemento array[7][2]
será: posición (array[0][0]) + 7 * 5 + 2”
Ejemplo:
int vect[10], mat[3][5], *p;
p=&vect[0];
printf (“%d”, *(p+2)); // imprime vect[2]
p=&mat[0][0];
printf (“%d”, *(p+2)); // imprime mat[0][2]
printf (“%d”, *(p+5)); // imprime mat[1][0]
printf (“%d”, *(p+14)); // imprime mat[2][4]
Relación Matrices ∼ Punteros I
El nombre de una matriz uni-dimensional (vector) es un puntero que apunta a la dirección de
memoria que contiene al primer elemento de la
matriz
Ejemplo:
double v [10]; // v es un puntero a v[0]
double *p;
p=&v[0];
// p = v
Es decir, como v apunta a v[0] tenemos que
*(v+4) es v[4]
Y recı́procamente: p[3] es lo mismo que v[3]
Resumiendo:
• *p ≡ v[0] ≡ *v ≡ p[0]
• *(p+1) ≡ v[1] ≡ *(v+1) ≡ p[1] . . .
Relación Matrices ∼ Punteros II
Sea la siguiente declaración:
int mat [5] [3], **p, *q;
El nombre de esta matriz (mat) es un puntero al
primer elemento de un vector de punteros (mat[
]), hay un vector de punteros con el mismo nombre que la matriz y el mismo número de elementos
que filas de la matriz cuyos elementos apuntan a
los primeros elementos de cada fila de la matriz
(fila de la matriz ∼ matriz unidimensional)
Ası́ pues mat es un puntero a una variable de tipo
puntero
Por tanto, mat es lo mismo que &mat[0] y mat[0]
lo mismo que &mat[0][0]. Análogamente, mat[1]
es &mat[1][0] . . .
Relación Matrices ∼ Punteros III
Si hacemos **p=mat; tendremos:
• *p ≡ mat[0]
• *(p+1) ≡ mat[1]
• **p ≡ mat[0][0]
• **(p+1) ≡ mat[1][0]
• *(*(p+1)+1) ≡ mat[1][1]
Relación Matrices ∼ Punteros IV
Si hacemos q=&mat[0][0]; podremos acceder
al elemento mat[i][j] ası́:
• *(q+j+i · 3)
• *(mat[i]+j)
• *(*(mat+i)+j)
• *(mat+i)[j]
Existe una única diferencia entre punteros y matrices, a saber, con los primeros se pueden definir estructuras equivalentes a matrices cuyas filas
puedan tener distinto número de elementos.
Variables y Parámetros en las Funciones I
En la definición de cualquier función suelen aparecer variables de tres tipos:
• auto (por defecto) solo visibles para la propia
función, se crean cada vez que se llama a la
función
• static solo visibles para la propia función, conservan su valor entre distintas llamadas a la
función
• extern son variables definidas fuera de la función, son visibles para la propia función
Llamaremos argumentos formales a los que aparecen en la primera lı́nea de la definición de la
función
Llamaremos argumentos actuales a los que aparecen en la llamada a la función desde el programa
que la realiza
Variables y Parámetros en las Funciones II
El valor de retorno de una función (es único) no
puede ser un array, aunque si un puntero o una
estructura (que puede contener arrays como elementos miembros)
En la llamada a una función los argumentos actuales son evaluados y se pasan copias de los mismos a las variables que constituyen los argumentos formales de la función (también se transfiere
el control de ejecución)
En consecuencia, los cambios que hagamos en
estas variables no se transmiten a aquellas que se
usaron en la llamada
El único valor que se puede transmitir al programa que realiza la llamada aparece tras algún
return
Diremos en este caso que se han pasado los argumentos POR VALOR
Variables y Parámetros en las Funciones III
Sea la función que pretende permutar los valores
de sus argumentos x e y:
void permutarmal (double x, double y)
{ double temp;
temp=x;
x=y;
y=temp; }
No funciona porque solo permuta los valores de
las copias que se le pasa en cada llamada, sin
embargo esta si:
void permutarbien (double *x, double *y)
{ double temp;
temp=*x;
*x=*y;
*y=temp; }
Variables y Parámetros en las Funciones IV
A la primera función (permutamal, paso de
parámetros por valor), se le llama con el nombre
de las variables que se pretenden permutar
A la segunda función (permutabien) se le pasa en
la llamada, la dirección de las variables
Diremos en este último caso que se han pasado
los argumentos POR REFERENCIA
• Ejemplo con arrays (función que multiplica una
matriz cuadrada por otra uni-dimensional):
void mult (int n, double a[ ][10], double
x[ ], double y[ ]) // posible declaración
void mult (int n, double (*a)[10], double
*x, double *y) // otra posible declaración
void mult (int n, double *a[10], double
*x, double *y) // declaración no válida
Descargar