Texto completo

(1)

Página 1 de 16

TEMA 8. Estructuras de Datos Complejas

8.1 Vectores Estáticos

Con las variables que conocemos hasta ahora, ¿ES POSIBLE almacenar la nota de todos los alumnos en UNA SOLA VARIABLE ?

NO

Necesitamos algo así:

Vble

5.6

7 3.9

.

.

.

6.2

Esta variable es un vector estático o array

Î

colección de variables

del mismo tipo que están almacenados en posiciones contiguas de memoria y que se referencian utilizando un nombre (que es común para todos los datos) y un índice (que indica la posición del dato dentro de todo el conjunto). La dirección más baja corresponde al primer elemento y la más alta al último.

Ejemplo:

Notas

5.6

7 3.9

.

.

.

6.2

0 1 2 Max-1

de modo que la nota del 3er alumno es: Notas[2]

(2)

Página 2 de 16 8.1.1 Vectores o Arrays unidimensionales

Gráficamente:

Vble

5 7 3

.

.

.

6

Indice Î 0 1 2 tamaño-1

El formato general para declarar un array unidimensional es

tipo nombre[tamaño];

Al declarar el array, se indica entre corchetes el número de elementos

que como máximo va a tener. Estos elementos serán del tipo indicado.

Ejemplo:

1. Declarar una variable capaz de almacenar las notas de 50 alumnos:

float NOTAS[50];

2. Suponiendo el vector relleno, mostrar las notas de todos los alumnos:

for (i = 0; i < 50; i++)

printf(“La nota del alumno %i es %.2f \n”, i+1, NOTAS[i]);

3. ¿Cuál es la nota media de la clase?

float MEDIA, TOTAL = 0;

for (i = 0; i < 50; i++)

TOTAL = TOTAL + NOTAS[i];

MEDIA = TOTAL / 50;

Con respecto a los arrays debemos tener en cuenta lo siguiente: • En C todos los arrays usan 0 como índice del primer elemento. • El C permite indexar un array por encima del nº de elementos que

contiene Æ no indica ningún mensaje de error en la compilación.

int main(void){

int edad[20], i;

for (i = 1; i <= 20; i++) { /* OJO aquí hay un fallo */

printf(“Dame la edad de la persona nº %i \n”, i);

scanf(“%i”, &edad[i]);

}

}

1. Error1: El índice del elemento 1º es el 0 (no el 1). 2. Error2: El último elemento es edad[19] (no edad[20]).

Al compilar no da error pero durante la ejecución, la ultima edad se introduce en la zona de memoria siguiente a la tabla, donde puede que haya alguna variable o parte del código binario del programa.

¿ Podría ser i <= 50 ?

(3)

Página 3 de 16 8.1.2 Vectores o Arrays multidimensionales

El formato general de una declaración de array multidimensional es:

tipo nombre[tamaño1][tamaño2]…[tamañoN];

El más usado es el array bidimensional o matriz (vector de vectores).

Ej: Almacenar la temperatura media de cada mes, durante 5 años (de 2001 a 2005):

años meses

float TEMP_MEDIA[5][12];

Mostrar la temperatura media de cada mes, para todos los años:

for (int agno = 0; agno < 5; agno++) Fijamos el año

{ printf(“En %i, las temperaturas fueron: \n”, 2001 + agno); for (mes = 0; mes < 12; mes++)

printf(“En el mes %i, %.2f grados \n”, mes + 1, TEMP_MEDIA[agno][mes]);

}

En los arrays bidimensionales el indice 1º indica la fila y el 2º la columna Ejemplo: almacenar en un array de 10 x 10 la tabla de multiplicar

int main(void)

{ int i, j, tabla[10][10];

for (i = 0; i < 10; i++)

{ for (j = 0; j < 10; j++)

tabla[i][j] = (i + 1) * (j + 1);

} }

Ej: Almacenar la temperatura media de cada día, de cada mes, para 5 años (de 2001 a 2005):

años meses días

float TEMPERATURA[5][12][30];

Mostrar la temperatura media de cada mes y año, para todos los años:

float media_mes, media_agno;

for (int agno = 0; agno < 5; agno++) Fijamos el año

{ media_agno = 0;

printf(“En %i, las temperaturas fueron: \n”, 2001 + agno);

for (mes = 0; mes < 12; mes++) { media_mes = 0;

for (dia = 0; dia < 30; dia++)

media_mes = media_mes + TEMPERATURA[agno][mes][dia]; media_mes = media_mes / 30;

printf(“En el mes %i, %.2f grados \n”, mes + 1, media_mes); media_agno = media_agno + media_mes;

}

media_agno = media_agno / 12;

printf(“En %i, la media fue %.2f \n”, 2001 + agno, media_agno);

}

Nos movemos por los meses de ese año

Nos movemos por los meses de ese año

(4)

Página 4 de 16 8.1.3 Inicialización de arrays (indicando y sin indicar el tamaño)

El formato general de una inicialización de un array es el siguiente:

tipo nombre[tamaño1]…[tamañoN] = { valor1, valor2, ..., valorN };

C permite la inicialización de los arrays globales y los locales static:

int i[5] = { 0, 1, 4, 9, 16 };

int i[] = { 0, 1, 4, 9, 16 }; /* sin indicar el tamaño */

Si no se indica el tamaño, se crea un array tan grande como para albergar todos los datos indicados (es más cómodo).

Las declaraciones anteriores son equivalentes a esta otra:

int i[5];

i[0] = 0; i[1] = 1; i[2] = 4; i[3] = 9; i[4] = 16;

Los arrays multidimensionales se inicializan de la misma manera (se deben indicar todas las dimensiones excepto la de más a la izquierda):

Indicando el tamaño Sin indicar el tamaño

int datos[3][2] = {1,1, 2,4, 3,9}; int datos[][2] = {1,1, 2,4, 3,9};

int datos[3][2] = { 1, 1,

2, 4,

3, 9 };

int datos[][2] = { 1, 1

2, 4,

3, 9 };

int datos[3][2];

datos[0][0] = 1; datos[0][1] = 1;

datos[1][0] = 2; datos[1][1] = 4;

(5)

Página 5 de 16 8.2 Cadenas o vector de caracteres

Una cadena o vector de caracteres (strings) es un vector que contiene en cada celda un carácter del código ASCII.

En C una cadena se implementa mediante un vector de caracteres

terminado en un nulo (‘\0’) que indica el final de la cadena

Í

Hay que

contar con él, a la hora de pensar en el tamaño del vector.

char cadena[11]; /* puede guardar una cadena de 10 caracteres */

Aunque C no tiene un tipo de datos string, sí que permite constantes de cadenas. Una constante de cadena es una lista de caracteres encerrada entre comillas dobles (“).

“Bienvenidos”

“Pulsa una tecla para continuar”

No se necesita añadir el nulo ('\0') manualmente en el final de las constantes de cadenas, el compilador hace esto automáticamente.

8.2.1 Inicialización de cadenas

Los arrays de caracteres permiten una inicialización abreviada:

char nombre[tamaño] = “cadena”;

El compilador automáticamente añade el terminador nulo:

char saludo[5] = “hola”;

char saludo[] = “hola”; // sin indicar el tamaño

char saludo[5] = { ‘h’, ‘o’, ‘l’, ‘a’, ‘\0’ }; // mas incómodo

Consideraciones a tener en cuenta al trabajar con cadenas:

1. No se pueden asignar cadenas con el operador = Î usar strcpy() 2. No se pueden comparar cadenas con el operador == Î usar strcmp()

3. No se puede asignar a una cadena un valor con el operador = salvo en la declaración de la misma.

(6)

Página 6 de 16 Para trabajar con cadenas, C dispone de la librería string.h:

Prototipo de función Acción:

char *strcpy(char *c1, const char *c2) Copia la cadena c2 sobre la cadena c1.

char *strcat(char *c1,const char *c2) Concatena la cadena c1 con la cadena c2

char *strchr(const char *c1, int ch1) Busca el carácter ch1 en la cadena c1. Devuelve un puntero a la 1ª ocurrencia de ch1 en c1. Si no lo encuentra devuelve NULL.

int strcmp(const char *c1, const char *c2) Compara la cadena c1 con la cadena c2. Devuelve un < 0 si c1 < c2; un > 0 si c1 > c2 y 0 si c1 es igual a c2.

int strlen(const char *c1) Devuelve la longitud de la cadena c1 sin contar el terminador nulo ('\0').

char *strupr(char *c1) Convierte la cadena c1 a mayúsculas.

char *strlwr(char *c1) Convierte la cadena c1 a minúsculas

char *strset(char *c1,int ch1) Rellena toda la cadena c1 con el carácter ch1

char *strstr(const char*c1, const char *c2) Encuentra la 1ª coincidencia de la subcadena c2 dentro de c1. Da NULL si no la encuentra.

Ejemplo:

#include <stdio.h> // printf(), scanf()

#include <conio.h> // getch()

#include <string.h> // strcpy(), strcmp(), strcat(), strlen()

int main(void) {

char nombre[30], cadena[80];

printf(“Dime tu nombre:”);

gets(nombre); /* scanf(“%s”,…) no lee espacios en blanco */ strcpy(cadena, nombre);

if (strcmp(nombre, cadena) == 0)

printf(“Las cadenas son iguales”);

printf(“mi nombre es %s \n”, nombre);

strcat(cadena,” es listo”); // concatena 'es listo' a cadena

printf(“%s”, cadena);

printf(“La frase tiene %i caracteres \n”, strlen(cadena));

strcpy(nombre, ””); // nombre[0] = '\0' (borra la cadena)

getch();

return 0;

(7)

Página 7 de 16 Para crear un array de cadenas, se usa una matriz de caracteres, en la que el tamaño del índice izquierdo determina el número de cadenas a almacenar y el tamaño del derecho especifica la longitud máxima de las cadenas a guardar.

int main(void) { int i;

char direcciones[10][50]; for (i = 0; i < 10; i++) {

printf(“Introduce la dirección nº %i:”, i + 1); scanf(“%s”, direcciones[i]);

}

for (i = 0; i < 10; i++)

printf("La dirección nº %i es: %s\n", i + 1, direcciones[i]); getch();

return 0; }

No hace falta poner el & en el scanf ya que las sentencias:

scanf(“%s”, direcciones[i]);

scanf(“%s”, &direcciones[i][0]);

son equivalentes: devuelven un puntero al elto 1º del array de índice i.

En C, el nombre de un array es un puntero al primer elemento del array. En los de 2 dimensiones, al indicar sólo el primer índice, es un puntero al primer elemento de dicho índice.

int main(void){

int i, vocal[5] = {0, 0, 0, 0, 0}; char frase[20][81], vocales[]=”aeiou”; for(i = 0; i < 20; i++){

printf(“Introduzca frase nº %d: “, i); gets(frase[i]); // gets(&frase[i][0])

}

for(i = 0; i < 20; i++){

switch ( toupper(frase[i][0]) ) {

case ‘A’: vocal[0]++; printf(“%s\n”, frase[i]); break;

case ‘E’: vocal[1]++; printf(“%s\n”, frase[i]); break; case ‘I’: vocal[2]++; printf(“%s\n”, frase[i]); break;

case ‘O’: vocal[3]++; printf(“%s\n”, frase[i]); break; case ‘U’: vocal[4]++; printf(“%s\n”, frase[i]);

}

}

for (i = 0; i < 5; i++)

printf(“%i frases empiezan por %c \n”, vocal[i], vocales[i]); return 0;

}

(8)

Página 8 de 16 8.3 Paso de cadenas y arrays a funciones

Para pasar un array completo como argumento a una función, en la llamada a la función sólo se pone el nombre del array (sin índice ni [ ]).

Si queremos pasar un único elemento a una función, se pondrá el nombre y entre corchetes el indice (nombre_array[índice_elemento]).

Los arrays se pasan siempre por referencia a una función: todos los cambios realizados a la tabla en la función afectan a la tabla original:

void ver(char c[20], int tope);

void cambia(char c[20], int salto);

int main(void) {

char letras[20];

int i, j =10;

for (i = 0; i < 20; i++)

letras[i] ='a' + i;

ver(letras, j);

cambia(letras, 5);

ver(letras, j);

return 0;

}

void ver(char c[20], int tope)

{ int i;

for(i =0; i < tope; i++)

printf("%c", c[i]);

printf("\n");

}

void cambia(char c[20], int salto)

{ int i;

for(i = 0; i < 20; i++)

c[i] = c[i] + salto;

}

Al ejecutar el programa, el resultado mostrado es el siguiente:

abcdefghij

fghijklmno

Para pasar arrays multidimensionales a funciones, se tiene que declarar todas excepto la dimensión de más a la izquierda.

Por ejemplo, si declaramos un array datos como:

int datos[4][10][3];

Entonces una función que reciba el array datos como parámetro podría declararse como:

int proceso(int m[ ][10][3]) { . . .

(9)

Página 9 de 16 Hay tres formas de declarar un parámetro tipo array:

1. Declarando el parámetro como un array indicando su tamaño 2. Declarando el parámetro como un array sin tamaño

3. Declarando el parámetro como un puntero

int main(void) {

float nota[10];

int i;

for(i =0; i < 10; i++) {

printf("\nDame la nota del alumno nº %d: ", i + 1);

scanf( "%f", &nota[i] );

}

aprobados(nota);

printf("\nPulse una tecla para continuar \n");

getch( ); return 0;

}

La función aprobados() puede implementarse de varias formas:

/* declarando el parámetro como un array indicando su tamaño */

void aprobados(float datos[10]) {

1 int i;

2 printf("Han aprobado los siguientes alumnos:\n");

3 for (i = 0; i < 10; i++)

4 if (datos[i] >= 5)

5 printf("Alumno nº %i: Nota: %5.2f \n", i + 1, datos[i] ); }

/* declarando el parámetro como un array sin tamaño */

void aprobados(float datos[ ]) { . . .

}

/* declarando como un puntero, accediendo como si fuera un array */

void aprobados(float *datos) { …

5 printf("Alumno nº %i: Nota: %5.2f \n", i + 1, *(datos + i) ); }

El código para estas 3 versiones sería el mismo.

(10)

Página 10 de 16 8.4 Estructuras

¿Podemos almacenar en una sola variable, de los tipos conocidos hasta ahora, el nombre de un alumno, y su nota media en un curso, p.ej. ?

NO

Necesitamos algo así

:

Vble

Esta variable es una

ESTRUCTURA

Colección de variables (miembros), que pueden ser de distinto tipo, que se referencian bajo el mismo nombre y un indentificador de campo.

Definición de una estructura:

struct nombre_estructura {

tipo campo1; Í Antes del main( )

tipo campoN;

};

Declaración de una variable del tipo anterior:

struct nombre_estructura nombre_variable;

Acceso a un campo miembro de una estructura:

nombre_variable.nombre_campomiembro;

Las estructuras:

Se pueden inicializar igual que los arrays.

Se pueden copiar y asignar, pasar a funciones como argumento y ser devueltas por éstas. Idem con los campos miembros.

No se pueden comparar. Para saber si dos estructuras son iguales hay que comparar uno a uno cada uno de sus miembros.

• Pueden contener como campo miembro otra estructura.

• Sus campos miembros pueden llamarse igual que una variable ordinaria o que otro campo miembro de otra estructura distinta.

Nombre

Nota_Media

Campos de la estructura

(11)

Página 11 de 16 Representar la información de una persona mediante una estructura:

/* definicion de la estructura direccion */ struct direccion {

char calle[30];

char ciudad[20];

char provincia[20];

};

/* definicion de la estructura datos_per */ struct datos_per {

char nombre[35];

struct direccion dir;

int edad;

};

/* declaracion de variables de tipo datos_per */ struct datos_per persona1, persona2;

En este ejemplo, si queremos pedir por teclado la calle y la edad de persona1 tendríamos que escribir lo siguiente:

gets(persona1.dir.calle); /* &persona1.dir.calle[0] */

scanf(“%i”, &persona1.edad);

8.4.1 Arrays de estructuras

Al igual que podemos declarar arrays de cualquier tipo de datos,

podemos declarar arrays de estructuras. Simplemente debemos definir primero la estructura y luego declarar una variable array de dicho tipo:

struct datos_per alumnos[100];

alumnos

0 1 … 99

nombre

calle ciudad prov

edad

. . .

Ejemplo:

Edad del último alumno: alumnos[99].edad; n

(12)

Página 12 de 16

#include <stdio.h> // printf(), scanf()

#include <stdlib.h> // system()

#include <conio.h> // getch()

struct direccion { /* definicion de la estructura direccion */

char calle[30];

char ciudad[20];

};

struct datos_per { /* definicion de la estructura datos_per */

char nombre[35];

struct direccion dir;

unsigned int edad;

};

int main(void) {

int n;

char tecla;

struct datos_per personas[5]; /* array de tipo datos_per */

system("cls"); // borra la pantalla

for (n = 0; n < 5; n++)

{ printf("Nombre: "); gets(personas[n].nombre); printf("Calle: "); gets(personas[n].dir.calle);

printf("Ciudad: "); scanf("%s", personas[n].dir.ciudad);

printf("Edad: "); scanf("%u", &personas[n].edad);

}

do {

do {

system("cls"); /* borra la pantalla */

printf("Dime el nº de la persona a visualizar: ");

scanf("%i", &n);

} while (n < 0 || n > 4);

printf("Nombre: %s\n", personas[n].nombre);

printf("Calle: %s\n", personas[n].dir.calle);

printf("Ciudad: %s\n", personas[n].dir.ciudad);

printf("Edad: %u\n", &personas[n].edad);

printf("\n¿Desea ver los datos de otra persona (s/n)?");

tecla = getch();

} while (tecla == 's'); return 0;

(13)

Página 13 de 16 8.4.2 Paso de estructuras a funciones

C permite pasar estructuras completas a funciones tanto por valor (por defecto) como por referencia (utilizando &).

Si la estructura es grande Î el paso por valor (copia) del parámetro struct puede llevar mucho tiempo Î En esos casos lo mejor es pasar la dirección de la estructura (por referencia).

Si pasamos un array de estructuras a una función, el array se pasa siempre por referencia (como todos los arrays), con lo que los cambios efectuados dentro de la función afectan al array original:

#include <stdio.h> // printf(), scanf()

#include <stdlib.h> // system()

#include <conio.h> // getch()

struct direccion {

char calle[30];

char ciudad[20];

};

struct datos_per {

char nombre[35];

struct direccion dir;

unsigned int edad;

};

int main(void) {

int n; char tecla;

struct datos_per personas[5];

system("cls"); // borra la pantalla

cargar_datos(personas);

do {

do {

system("cls");

printf("nº de la persona a ver: "); scanf("%i", &n);

} while (n < 0 || n > 4);

ver_datos(personas[n]);

printf("\n¿Desea ver los datos de otra persona (s/n)?");

tecla = getch();

} while (tecla == 's');

return 0;

(14)

Página 14 de 16

void cargar_datos(struct datos_per p[]) {

int n;

for (n = 0; n < 5; n++) {

printf("Nombre: "); scanf("%s", p[n].nombre);

printf("Calle: "); scanf("%s", p[n].dir.calle);

printf("Ciudad: "); scanf("%s", p[n].dir.ciudad);

printf("Edad: "); scanf("%u", &p[n].edad);

}

}

void ver_datos(struct datos_per p) {

printf("Nombre: %s\n", p.nombre);

printf("Calle: %s\n", p.dir.calle);

printf("Ciudad: %s\n", p.dir.ciudad);

printf("Edad: %u\n", p.edad);

}

8.4.3 Paso de elementos de estructuras a funciones

Además de pasar una estructura completa a una función, podemos

pasar sólo un campo miembro de una estructura.

Si el campo miembro pasado a la función es:

• Una variable u otra estructura Æ se pasa por valor Para pasarlo por referencia hay que utilizar el &. • Un array Æ se pasa siempre por referencia

Las siguientes llamadas sólo pasan un campo miembroro de la estructura:

funcion1(personas[2].nombre); /* pasa sólo el campo nombre */

funcion2(personas[2].edad); /* pasa sólo el campo edad */

funcion3(personas[2].dir); /* pasa sólo el campo dir que es otra estructura*/

funcion4(personas[2].dir.calle); /* pasa sólo el campo calle */

Los prototipos de estas funciones serían los siguientes:

void funcion1(char *cadena);

void funcion2(int n);

void funcion3(struct direccion d);

(15)

Página 15 de 16 Si lo que queremos es pasar un elemento por referencia, debemos pasar la dirección de dicho elemento:

funcion1(personas[2].nombre); /* pasa por referencia el campo nombre */

funcion2(&personas[2].edad); /* pasa por referencia el campo edad */

funcion3(&personas[2].dir); /*pasa por referencia el campo (estructura) dir*/

funcion4(personas[2].dir.calle); /* pasa por referencia el campo calle */

Los prototipos de estas funciones serían los siguientes:

void funcion1(char *cadena);

void funcion2(int *n);

void funcion3(struct direccion *d);

void funcion4(char cadena[ ]);

Cuando uno de los elementos de la estructura que pasamos es una

cadena de caracteres, no hace falta utilizar el operador & ya que los arrays se pasan siempre por referencia y no por valor.

8.4.4 Punteros a estructuras

De la misma forma que podemos usar punteros a cualquier tipo de

variable, podemos usar punteros a variables de estructuras.

Para declarar un puntero a una estructura de tipo struct datos_per:

struct datos_per *p;

La dirección de una variable de estructura, se obtiene, al igual que otro tipo de variable, anteponiendo el operador & delante de su nombre:

struct datos_per persona, *p;

p = &persona; /* coloca la dir de la estructura persona en p */

Cuando se usa punteros a estructuras no se debe usar el operador

punto ‘.’ para acceder a un elemento de la estructura a través del puntero. En su lugar se debe usar el operador ‘->’:

p->nombre; /* equivalente a (*p).nombre; */

p->edad; /* equivalente a (*p).edad; */

(16)

Página 16 de 16

#include <stdio.h> #include <conio.h>

struct direccion { /* definicion estructura direccion */

char calle[30]; char ciudad[20]; char provincia[20];

};

struct datos_per { /* definicion estructura datos_per */

char nombre[35]; struct direccion dir; unsigned int edad;

};

void cargar_datos(struct datos_per *p); int main(void) {

int n;

struct datos_per personas[5];

for (n = 0; n < 5; n++)

cargar_datos(&personas[n]);

printf("Pulse una tecla para terminar"); getch(); return 0;

}

void cargar_datos(struct datos_per *p) {

printf("Nombre: "); scanf("%s", p->nombre); printf("Calle: "); scanf("%s", p->dir.calle); printf("Ciudad: "); scanf("%s", p->dir.ciudad);

printf("Provincia: "); scanf("%s", p->dir.provincia); printf("Edad: "); scanf("%u", &p->edad);

}

8.5 Typedef

Permite crear un 'alias' de un tipo de C o para uno creado con struct.

Sintaxis: typedef tipo_existente alias;

typedef unsigned char byte;

byte dato; /* unsigned char dato; */

Podemos usar typedef para crear alias para estructuras. Por ejemplo:

typedef struct { char calle[30];

char ciudad[20];

}t_direccion;

typedef struct { char nombre[35]; t_direccion dir;

unsigned int edad; } t_datos_per;

Figure

Actualización...

Referencias

Actualización...