¡Descarga TIPOS DE DATOS ESTRUCTURADOS y más Apuntes en PDF de Fundamentos de Electrónica solo en Docsity!
UNIVERSIDAD DE MALAGA
DPTO. DE LENGUAJES Y C. DE LA COMPUTACION E.T.S. DE INGENIERIA INFORMATICA
FUNDAMENTOS DE LA PROGRAMACIÓN
T TEEMMAA IIVV
TIPOS DE DATOS ESTRUCTURADOS
IV.1. Registros.
IV.1.1. Tipo de Datos struct
IV.1.2. Registros como Parámetros.
IV.1.3. Ejemplo con registros.
IV.2. Arrays.
IV.2.1. Tipo de Datos array.
IV.2.2. Arrays como Parámetros.
IV.2.2.1. Ejemplo con arrays unidimensionales.
IV.2.3. Problemas comunes al trabajar con arrays.
IV.2.4. Utilidad de los arrays.
IV.2.5. Arrays Multidimensionales.
IV.2.5.1 .Ejemplo con Arrays Multidimensionales.
IV.2.6. Arrays Abiertos como Parámetros.
IV.2.6.1. Ejemplo de Arrays Abiertos.
IV.3. Cadenas de caracteres.
IV.3.1. Tipo de datos string.
IV.3.2. String s como Parámetros.
IV.3.2.1. Ejemplo con string
IV.4. Resolución de Problemas usando Tipos Estructurados
_______________________________________
En el tema 2 de esta asignatura Fundamentos de la Programación hemos estudiado los tipos
de datos simples en C++. En este tema abordaremos los tipos de datos estructurados, es decir,
aquellos que se definen como composición de otros tipos de datos ya definidos, ya sean
simples o estructurados. En C++ los tipos estructurados son el registro, el tipo array y la
cadena de caracteres.
IV.1. Registros
Un registro es una colección de elementos que pueden ser de tipos distintos. O lo que es lo
mismo, en la estructura registro no existe un único tipo base. A cada componente de la
estructura se le llama campo, y podrá ser de cualquier tipo (simple o estructurado).
IV.1.1 Tipo de Datos struct
Para definir un tipo registro se utilizará la palabra reservada struct seguida del
identificador del tipo y de la relación de componentes entre llaves, terminada en ';'. Para cada
componente se indica el tipo e identificador del componente terminado en ';'. Ejemplo:
// tipo enumerado enum Tmes { enero, febrero, marzo, abril, mayo, junio, Julio, agosto, sepiembre, octubre, noviembre, diciembre }; // tipo registro struct TFecha { unsigned dia; Tmes mes; unsigned agno; };
Una vez definido un tipo registro se pueden declarar variables y constantes de dicho
tipo:
const TFecha HOY={16, diciembre, 2010} TFecha f_nac,f_ant;
Los identificadores dia, mes y agno representan los nombres de los elementos
componentes del registro TFecha, denominados campos. Los nombres de los campos de un
registro son locales a él, por lo que no hay conflicto con otros nombres en el programa.
Una referencia a un campo de un registro consiste en el nombre de la variable (o
constante) registro y el nombre del campo, separados por un punto.
_______________________________________
// escritura del registro campo a campo }
Un registro también puede ser el valor devuelto por una función. Pero por las mismas
razones de eficiencia expuestas anteriormente respecto al paso de parámetros, es conveniente
que el valor devuelto por una función no sea de un tipo estructurado. En ese caso, será
preferible construir un procedimiento y que el valor se devuelva como parámetro de salida
mediante paso por referencia.
void leer_fecha(TFecha& fech) { // lectura del registro campo a campo }
En resumen, las operaciones que se pueden realizar sobre registros completos son la
asignación, la inicialización de una variable o constante de tipo registro, y el paso como
parámetro a subprogramas, bien por referencia si el parámetro es de salida o entrada/salida o
por referencia constante, caso de ser parámetro de entrada
IV.1.3 Ejemplo con registros
Suponemos definida la siguiente estructura registro que representa un instante de tiempo:
struct TTiempo{ unsigned horas,minutos,segundos;
};
Ejemplo: Función para calcular los segundos transcurridos entre dos instantes de
tiempo dados
unsigned convsegundos ( const TTiempo& t) { return t.horas3600 + t.minutos60 + t.segundos; } unsigned calcsegundos ( const TTiempo& t1, const TTiempo& t2) { return convsegundos(t1)-convsegundos(t2); }
Ejemplo: Procedimiento para convertir un tiempo expresado en segundos a formato de
horas, minutos, segundos.
void convtie mpo ( unsigned seg, TTiempo& t) { t.horas= seg /3600; seg= seg % 3600; t.minutos = seg /60; t.segundos= seg % 60; }
_______________________________________
IV.2. ARRAYS
El array es un tipo de datos estructurado formado por un número fijo de elementos del
mismo tipo. El tipo de los elementos del array se llama tipo base y puede ser cualquier tipo.
Una estructura array se caracteriza porque se puede acceder de manera directa a sus
elementos, a través de los valores de un tipo denominado índice. El tipo índice debe ser un
ordinal de cardinalidad finita. El tipo base puede ser tanto simple como estructurado. En
resumen, un array es una colección finita de datos homogéneos y ordenados:
Homogéneos: Todos los elementos que hay dentro de un array son del mismo tipo
(tipo base)
Finita: El número de elementos de un array es finito
Tamaño fijo (Dimensión): El número de elementos de un array se establece en su
definición y se conoce como dimensión del array. Esta dimensión establece también los
valores del tipo índice del array (valores desde 0 hasta dimensión).
Ordenados: Los elementos de un array mantienen una posición concreta en función
de los valores del tipo índice del array.
IV.2.1 Tipo de Datos array.
Para definir un tipo array se usa la palabra reservada typedef seguida por el tipo de
los elementos (tipo base ), el identificador del nuevo tipo array , y entre corchetes el número
de elementos que lo componen (dimensión).
const int MAX_CARACTERES = 30; typedef char TNombre[MAX_CARACTERES]; // Tipo Array de 30 char typedef int TVector[10]; // Tipo Array de 10 int TVector v1; // variable de tipo Vector TNombre nom; // variable de tipo Nombre
Para acceder a cada elemento de una variable declarada de tipo array se usa su
nombre seguido por una expresión entre corchetes (índice). El valor de dicha expresión
indicará la posición en el array del elemento al que se quiere acceder.
El primer elemento de un array ocupa la posición 0 (v1[0] y nom[0] en el
ejemplo), y el último elemento ocupa la posición dimensión– 1 (v1[9] y
nom[MAX_CARACTERES–1] en el ejemplo). Así mismo, es posible que el índice de acceso al
elemento sea una expresión: nom[i], nom[i+1], v1[i*2], etc.
_______________________________________
using namespace std; typedef int TVector[10]; // indexado desde la posición 0 a la 9
void LeerVector (TVector& v) // lee valores para cada componente del vector { for ( unsigned i=0; i<10;i++){ cout<<"Introduzca v["<<i<<"]: "; cin>>v[i]; } } void ImprimirVector ( const TVector& v) // muestra por pantalla los valores del vector { for ( unsigned i=0; i<10;i++){ cout<<"v["<<i<<"]: "; cout<<v[i]<<endl;; } } int main () { TVector v1; LeerVector(v1); ImprimirVector(v1); return 0;
}
IV.2.3 Problemas comunes al trabajar con arrays:
Inicialización: Las variables de tipo array , al igual que cualquier variable, deben ser
inicializadas, bien mediante lectura por teclado de cada una de sus componentes o bien
mediante asignaciones componente a componente.
Índice fuera de rango: Siempre hay que comprobar que se accede a una posición
válida dentro del array. El lenguaje de programación C++ no comprueba que el valor del
índice se encuentre dentro del rango válido para acceder a un elemento de un array. En caso
de acceso (tanto para consultar su valor, como para modificarlo) fuera del rango válido,
pueden ocurrir errores inesperados durante la ejecución del programa. De hecho, puede
suceder que el error se manifieste en otro punto del programa, e incluso que no se manifieste
visiblemente, y que permanezca oculto. Este hecho puede dar lugar a errores muy difíciles de
detectar.
IV.2.4 Utilidad de los arrays
Consideremos el siguiente ejemplo: Una empresa tiene en plantilla 20 agentes de
ventas (identificados por números del 1 al 20) que cobran comisión sobre la parte de sus
operaciones comerciales que excede los 2/3 del promedio de ventas del grupo.
_______________________________________
Se necesita un algoritmo que lea el valor de las operaciones comerciales de cada
agente e imprima el número de identificación de aquellos que deban percibir comisión así
como el valor correspondiente a sus ventas.
Este problema tiene 2 aspectos que juntos lo hacen difícil de programar con los
mecanismos vistos hasta ahora:
Procesamiento similar sobre los datos de cada agente:
leer ventas
calcular promedio de ventas
comparar niveles
decidir si debe o no recibir comisión
Se necesita almacenar durante la ejecución del programa los valores de las ventas de
cada agente: para el cálculo del promedio y para la comparación de niveles. Esto implica que
necesitamos 20 variables para retener los valores de las ventas.
Un posible algoritmo sería:
#include using namespace std; const float PORCION=2.0/3.0; int main () { float ventas1,ventas2,ventas3, ventas4,...ventas20; // ventas realizadas por cada agente float suma=0.0; // acumulado de ventas float umbral; // valor minimo para obtener comision cout<<"Introduzca las ventas del agente: "<<endl; cin>>ventas1; suma= suma+ventas1; cout<<"Introduzca las ventas del agente: "<<endl; cin>>ventas2; suma=suma+ventas2; cout<<"Introduzca las ventas del agente: "<<endl; cin>>ventas3; suma= suma+ventas3; cout<<"Introduzca las ventas del agente: "<<endl; cin>>ventas4; suma= suma+ventas4; ... cout<<"Introduzca las ventas del agente: "<<endl; cin>>ventas20; suma= suma+ventas20; umbral= PORCION*(suma/20.0); if (ventas1>umbral) { cout<<"Ventas del Agente 1: "<<ventas1; } if (ventas2>umbral)
_______________________________________
float calcMedia ( const TVentas& ventas) { float suma=0.0; for ( unsigned i=0; i<NUMAGENTES; i++) { suma= suma+ventas[i]; } return suma/NUMAGENTES; } void imprimir ( const TVentas& ventas, float umbral) { for ( unsigned i=0; i<NUMAGENTES; i++) { if (ventas[i]>umbral) { cout<<"Ventas del Agente "<<i+1<<" :"<<ventas[i]; } } }
int main () { TVentas ventas; float umbral; leerVentas(ventas); umbral= PORCION*calcMedia(ventas); imprimir(ventas,umbral); return 0; }
IV.2.5 Arrays Multidimensionales
Al definir el tipo array intervienen siempre 2 tipos : el tipo indice y el tipo base. El
tipo indice ha de ser un ordinal de cardinalidad finita, pero sobre el tipo base no existe
restricción alguna. Al poder usarse cualquier tipo como tipo base de un array , esto incluye la
posibilidad de usar tipos estructurados como tipo base de un array. De esta forma, en caso de
que el tipo base de un array sea a su vez otro array , obtendremos array s multidimensionales.
Por ejemplo:
const unsigned Filas=5; const unsigned Columnas=3; typedef int TVectorFila[Columnas]; typedef TVectorFila TMatriz[Filas]; // matriz de 5 Vectores Fila
También es posible declararlo de la siguiente forma:
const unsigned Filas=5; const unsigned Columnas=3; typedef int TMatriz[Filas][Columnas]; // matriz 6x
Si declaramos la variable
_______________________________________
TMatriz a;
“a” será una estructura que puede contener 5 elementos de tipo TVectorFila, o lo que
es lo mismo, 15 elementos de tipo int. Gráficamente:
5 filas
3 columnas
Elementos de tipo
TVectorFila
Elementos de tipo int
A este tipo de estructuras se les denomina Array s bidimensionales. Los array s
bidimensionales se utilizan a menudo para representar matrices matemáticas, de igual forma
que un array (unidimensional) puede emplearse para representar un vector.
Podemos declarar una variable del tipo especificado anteriormente, y acceder a los
componentes, tanto individualmente como por filas:
TMatriz a; a[2] // fila 2, es decir, un array de 3 elementos de tipo int a[2][3] // un elemento del tipo base int del array a, en concreto el // entero almacenado en la fila 2, columna 3 del array
De igual forma, si hemos definido el array bidimensional TMatriz, podríamos
construir un nuevo tipo array cuyo tipo base sea TMatriz, y obtendríamos un array de 3
dimensiones. Y así sucesivamente.
const unsigned Profundidad=3; const unsigned Filas=5; const unsigned Columnas=3; typedef int TVectorFila[Columnas]; typedef TVectorFila TMatriz[Filas]; // array de 6 Vectores Fila
_______________________________________
int resultado=0; for ( unsigned co=0; co<Columnas; co++) { resultado+= fila[co]; } return resultado; }
int sumarCol ( const TMatriz& mat, unsigned co) { int res=0; for ( unsigned fi=0; fi<Filas; fi++) { res=res+ mat[fi][co]; } return res; }
int main () { TMatriz a; TVectorColumna b; TVectorFila c; leerMatriz(a); for ( unsigned fi=0; fi<Filas; fi++) { b[fi]= sumarFila(a[fi]); } for ( unsigned co=0; co<Columnas; co++) { c[co]= sumarCol(a,co); } for ( unsigned fi=0; fi<Filas;fi++) { escribirFila(a[fi]); cout<<b[fi]; cout<<endl; } escribirFila(c); return 0; }
IV.2.6 Arrays Abiertos como Parámetros
Hay situaciones en las que deseamos implementar un subprograma que trabaje con
array s, independientemente de su tamaño. De forma tal que el tamaño del array en el
subprograma (parametro formal) se adecue al tamaño del array pasado durante la invocacion
al mismo (parametro real). Para ello, el parámetro formal se declara especificando su tipo
base, el identificador y el símbolo [] que indica que dicho parámetro es un array sin tamaño
especificado, su tamaño dependerá del tamaño del parámetro real especificado en cada
invocación al subprograma. El tipo base del parámetro real debe coincidir con el del
parámetro formal. La información sobre el tamaño del array se pierde al pasarlo como array
abierto, por lo que dicho tamaño se deberá también pasar como parámetro.
_______________________________________
En caso de pasar array s multidimensionales como parámetros abiertos, sólo es posible
poner la primera dimensión sin especificar.
El paso se realiza siempre por referencia sin necesidad de especificar el símbolo & , y
para asegurar que no sea modificado en caso de información de entrada, se realizará el paso
de parámetros constante.
IV.2.6.1 Ejemplo de Arrays Abiertos
#include using namespace std;
const unsigned TAMANO_1 = 10; const unsigned TAMANO_2 = 20; typedef int Tvector_1[TAMANO_1]; typedef int Tvector_2[TAMANO_2];
void imprimir ( int n, // numero de elementos const int vct[]) // Parámetro de entrada: vector de enteros // paso por referencia constante { for ( int i = 0; i < n; ++i) { cout<<" v["<< i <<"]: "; cout << vct[i] << " " ; } cout << endl; }
void valores ( int n, // numero de elementos int vct[]) // vector de enteros (paso por referencia) { for ( int i = 0; i < n; ++i) { cout<<"Introduzca componente v["<<i<<"]: "; cin >> vct[i]; } }
int main () { Tvector_1 vector_1; Tvector_2 vector_2; cout<<"Lectura de valores para el array 1: "<<endl; valores(TAMANO_1, vector_1); cout<<"Escritura de los valores del array 1: "<<endl; imprimir(TAMANO_1, vector_1); cout<<"Lectura de valores para el array 2: "<<endl; valores(TAMANO_2, vector_2); cout<<"Escritura de los valores del array 2: "<<endl; imprimir(TAMANO_2, vector_2); return 0; }
Sólo es válido declarar array s abiertos como parámetros. No es posible declarar
variables como array s abiertos.
_______________________________________
string cadena; string nmb; cadena = nombre; // asigna el valor de nombre a cadena
Concatenación de dos cadenas ( + ):
En la concatenación, al menos uno de los dos primeros operandos debe estar
declarado de tipo string.
La operación de concatenación será imprescindible para añadir caracteres a una
cadena porque no es posible acceder fuera del rango de la misma.
cadena += ' '; // añade un espacio al final de cadena cadena += "garcia"; // cadena valdra "jose garcia" nmb = cadena + ' ' + "lopez"; // nmb = "jose garcia lopez" nmb = "pepe" + "luis"; // ERROR
- Comparación lexicográfica:
Es posible comparar variables y constantes de tipo string mediante los operadores
relaciones (== != > >= < <=) siempre y cuando al menos uno de los operandos esté
declarado como de tipo string :
if (nombre == "jose") { ... } if (nombre != "jose") { ... } if (cadena > nombre) { ... } if (nombre >= "jose") { ... } if (cadena < nombre) { ... } if (nombre <= "jose") { ... } if ("pepe" == "pepe") { ... } // ERROR
También se puede utilizar la función compare que devuelve tres posibles
valores:
int res; res = cadena.compare(nombre); // si cadena < nombre - > res < 0 // si cadena == nombre -> res == 0 // si cadena > nombre -> res > 0
Longitud:
Para saber el número de caracteres de un string se utiliza size
int n = cadena.size();
Acceso al i-ésimo carácter de una cadena ([i]), donde i [0..nombre.size() - 1]
Se accede a los caracteres que la componen mediante indexación donde el primer
elemento se encuentra en el índice 0 , y el último elemento en el índice size()-1:
string cadena = "pepe garcia"; // string constante char primera_letra = cadena[0]; // toma valor 'p' char ultima_letra = cadena[cadena.size()-1]; // toma valor 'a' cadena[0] = ‘P’; // pone el primer carácter de cadena a ‘P’
_______________________________________
- NOTA IMPORTANTE: C++ no comprueba que el acceso a los elementos de una
cadena mediante la indexación (vista anteriormente) se realiza dentro del rango
correcto, por lo que un error en dicha indexación puede dar lugar a errores
inesperados durante la ejecución del programa.
- Obtener una subcadena a partir de una cadena ([i,t]).
i [0..cadena.size() - 1] y t [0..cadena.size() - i]
Para obtener una subcadena a partir de una cadena dada se utiliza la operación substr
de la siguiente forma. Dada una cadena cad, obtenemos una subcadena a partir de la posición
i de la misma y con un tamaño t mediante:
cad.substr(i,t)
Por ejemplo:
string cadena = “Jose Antonio”; string seg_nombre; seg_nombre = cadena.substr(5,7); // obtenemos “Antonio” // subcadena de tamaño 7 a partir de la posición 5
Realizar la entrada y salida de string mediante los operadores habituales:
cout << "Introduce nombre: "; cin >> cadena; // salta espacios y lee hasta siguiente espacio cout << " Nombre: " << cadena << endl;
El operador de entrada (>>) salta los espacios en blanco (y saltos de línea) hasta
encontrar una cadena de caracteres, dicha cadena se leerá hasta encontrar un espacio en
blanco o un salto de línea. Así mismo, tambien es posible leer una línea entera hasta el salto
de línea, u otro delimitador que se especifique:
getline(cin, cadena); // lee una linea completa getline(cin, cadena, ';'); // lee hasta ';'
Una cuestión importante a tener en cuenta son las situaciones en las que se puede
quedar la información en el buffer dando lugar a errores en la toma de datos.
La lectura de datos mediante flujos (>>) ignora los separadores existentes entre los
diferentes elementos de entrada. Sin embargo, esto no es así para la operación getline. Esta
operación lee del buffer de teclado la información que se encuentre sin diferenciar entre
separadores y otros datos.
Por ejemplo, supongamos que tenemos la siguiente combinación de operaciones:
string nombre, apelidos; cin >> nombre; getline(cin, apellidos);
y la entrada para estas operaciones es:
_______________________________________
IV.3.2.1 Ejemplo con string
Se desea leer una cadena de caracteres, y copiarla en orden inverso en otra cadena.
#include #include using namespace std; void invertirCadena ( const string& palabra, string& invertida) {
for ( int i=palabra.size()-1;i>=0;i--) { invertida= invertida+palabra[i]; } } int main () { string entrada, invertida; cout<<"Escribir la palabra: "; cin>>entrada; invertirCadena(entrada, invertida); cout<<"La palabra "<<entrada<<" invertida es: "<<invertida<<endl; return 0; }
IV.4. Resolución de Problemas usando Tipos Estructurados
Implementación de una Agenda personal: Se pretende diseñar un programa para la gestión de
una 'agenda'. La información personal que será almacenada es la siguiente:
Nombre
Teléfono
Dirección
Calle
Número
Piso
Código Postal
Ciudad
Las operaciones a realizar con dicha agenda serán:
1. Añadir los datos de una persona
2. Acceder a los datos de una persona a partir de su nombre.
_______________________________________
3. Borrar una persona a partir de su nombre.
4. Modificar los datos de una persona a partir de su nombre.
#include #include using namespace std; unsigned const MAXPERSONAS = 50; int const NO_VALIDA = -1; // Indica posición no valida del array struct TDireccion { unsigned num; string calle; string piso; string cp; string ciudad; };
struct TPersona { string nombre; string tel; TDireccion direccion; };
typedef TPersona TPersonas[MAXPERSONAS]; / Array de 50 registros TPersona
struct TAgenda { unsigned num_pers; //número real de personas almacenada TPersonas pers; //datos de dichas personas };
void Inicializar (TAgenda& ag) { ag.num_pers=0; // agenda con 0 registros TPersona almacenados }
void LeerDireccion (TDireccion& dir) { cout<<"Introduzca calle: "; cin>>dir.calle; cout<<"Introduzca número: "; cin>>dir.num; cout<<"Introduzca piso: "; cin>>dir.piso; cout<<"Introduzca código postal: "; cin>>dir.cp; cout<<"Introduzca ciudad: "; cin>>dir.ciudad; }
void LeerPersona (TPersona& per) { cout<<"Introduzca nombre: "; cin>>per.nombre; cout<<"Introduzca telefono: "; cin>>per.tel; LeerDireccion(per.direccion); } void EscribirDireccion ( const TDireccion& dir)