I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
FUNDAMENTOS DE PROGRAMACIÓN Tema 5
Estructuras Estáticas
1º Administración de Sistemas Informáticos I.E.S. Francisco Romero Vargas Departamento de Informática __________________________________________________________________________________________________________ Estructuras Estáticas 1
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
1. INTRODUCCIÓN. Hasta ahora, los datos manejados en los programas han sido los denominados datos simples (numéricos, carácter, ...). En numerosas ocasiones es necesario utilizar un conjunto de datos relacionados entre sí. Por ejemplo, si se quiere manipular una lista de 100 edades de personas, es conveniente tratar este conjunto de datos de forma unitaria en lugar de utilizar 100 variables, una para cada edad. Un conjunto de datos homogéneos (del mismo tipo) que se tratan como una sola unidad se denomina estructura de datos. Si una estructura de datos reside en la memoria central del ordenador se denomina estructura de datos interna. Recíprocamente, si reside en un soporte externo, se denomina estructura de datos externa. Las estructuras de datos internas pueden ser de dos tipos: - Estáticas:
Tienen un número fijo de elementos que queda determinado desde la declaración de la estructura en el comienzo del programa. - Dinámicas: Tienen un número de elementos que varía a lo largo de la ejecución del programa. La estructura de datos interna más importante, desde el punto de vista de su utilización, es la tabla (array o arreglo), que existe en casi todos los lenguajes de programación. Esta estructura se corresponde con los conceptos matemáticos de vector (1 dimensión), matriz (2 dimensiones) y poliedro (3 o más dimensiones). Una tabla consiste en un número fijo y finito de elementos, todos del mismo tipo y bajo un nombre común para todos ellos. Nombre 0 1 2 3 4 5 6 7 ... N-1 (índice) ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐ │Val.1│Val.2│Val.3│Val.4│Val.5│Val.6│Val.7│Val.8│ ... │Val.N│ └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘ ↑ ↑ 1.er elemento enésimo elemento
El nombre de la tabla es un identificador común para todos sus elementos distinguiéndose cada uno por una lista de índices que complementan a dicho nombre para referenciarlos individualmente. Se denomina componentes a los elementos de una tabla. Una tabla se puede estructurar en una, dos o más dimensiones, según el número de índices necesarios para acceder a sus elementos. Por tanto, la dimensión de una tabla coincide con el número de índices que utiliza. Longitud o tamaño de una tabla es el número de componentes que contiene. El tipo de una tabla es el tipo de sus componentes (numéricos, carácter, cadena, etc). Los componentes de una tabla se utilizan de la misma forma que cualquier otra variable de un programa, pudiendo por tanto intervenir en instrucciones de asignación, entrada/salida, etc. Antes de utilizar una tabla habremos de definirla en el entorno. Las tablas se clasifican según su dimensión en: __________________________________________________________________________________________________________ Estructuras Estáticas 2
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
- Unidimensionales - Bidimensionales - Multidimensionales
2. DEFINICIÓN Y CONCEPTO DE TABLA. Cuando en un programa el número de datos del mismo tipo, que mantengan una relación “ordenada” entre ellos, aumenta un poco, no interesa definir una variable para cada uno de ellos ya que la proliferación de las mismas llevaría a la confusión y resultaría dificultoso su manejo. Por ejemplo, para hallar la media de diez notas no sería eficiente definir diez variables de la forma n1, n2, n3 ...n10. Por tanto, para manejar en memoria interna un número mediano o grande de datos es conveniente recurrir a estructuras complejas de datos como son las tablas. Se denomina tabla a un conjunto finito y fijo de elementos del mismo tipo, que se hallan almacenados en posiciones consecutivas de memoria. 1. Cada uno de los elementos de una tabla se referencia por un nombre común a todos ellos y por uno o varios índices particulares, que determinan su posición relativa en la memoria, dentro del conjunto de elementos de la tabla. Hay que tener en cuenta que en C el primer índice que se puede utilizar es el que tiene el valor 0 y que el nombre de una tabla es un puntero a la dirección de memoria de comienzo de la tabla . Ejemplo:
Para guardar diez notas emplearemos el vector notas[10] con capacidad para 10 notas. La primera nota se guarda en notas[0], la segunda en notas[1],...y la décima en notas[9].
2. Todos los elementos de la tabla son del mismo tipo. 3. El nombre de una tabla (idéntico al de cada uno de sus elementos) sigue las mismas reglas de formación que conocemos para identificadores de variables. 4. Los índices, dependiendo del lenguaje de programación que se utilice, pueden ir encerrados entre paréntesis o entre corchetes. Concretamente, el lenguaje C utiliza corchetes. 5. Los índices deben ser números enteros; señalan el orden relativo en que están almacenados los elementos de la tabla. El número de índices determina la dimensión de la tabla: 1 sólo índice = unidimensionales o vectores. 2 índices = bidimensionales o matrices. 3 índices = tridimensionales o poliedros. 6. Se llama longitud o tamaño de la tabla al número total de sus elementos. Ejemplo:
Supongamos que en un programa necesitamos operar con la cantidad de lluvia caída en una ciudad (en litros por metro cuadrado) durante un mes. Para ello declaramos una tabla
__________________________________________________________________________________________________________ Estructuras Estáticas 3
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
unidimensional (vector) numérico real de 31 elementos que se llamará ltrs_dia. Se podría representar así: 10.455 ltrs_dia[0]
0.389 ltrs_dia[1]
1.923 ltrs_dia[2]
5.299 ... ltrs_dia[3] ...
Para acceder a la lluvia caída el 4º día escribiríamos ltrs_dia[3] 7. La tabla es una estructura interna, es decir, existe como tal estructura en la memoria principal del ordenador. Como ya sabemos, la memoria principal tiene una capacidad limitada, por tanto, las tablas serán conjuntos finitos. 8. La tabla es un conjunto fijo de elementos y, evidentemente, el número de estos elementos depende del valor definido en el programa. Dicho en otras palabras, el espacio que ocupará en memoria la tabla cuando el programa se esté ejecutando no podrá cambiarse. Si se necesitara un número distinto de elementos (otro tamaño) sería necesario editar el programa fuente, cambiar la definición de la tabla y volver a traducir el programa. Por esto se dice que las tablas son estructuras estáticas. Ejemplo:
Si un programa necesitase operar con la temperatura media en una ciudad a lo largo del año necesitaría una tabla bidimensional (matriz) numérico real de 12 x 31 elementos que podría llamarse Tmp. Podría representarse como una matriz matemática, donde cada fila correspondería a un mes y cada columna a un día. Los nombres de los elementos representados en la tabla son:
0 1 2 3 MESES
11
Tmp[0][0] Tmp[1][0] Tmp[2][0] Tmp[3][0] ... Tmp[11][0]
DIAS 0 1 Tmp[0][1] Tmp[1][1] Tmp[2][1] Tmp[3][1] ... Tmp[11][1]
2 Tmp[0][2] Tmp[1][2] Tmp[2][2] Tmp[3][2] ... Tmp[11][2]
... ... ... ... ... ...
... 30 Tmp[0][30] Tmp[1][30] Tmp[2][30] Tmp[3][30] ... ... Tmp[11][30]
El 1º índice representa la fila, y el 2º, la columna a que pertenece. Para referenciar a la temperatura media del 3º día del 4º mes escribiríamos: Tmp[3][2]. Sin embargo, la distribución de datos realizada en este ejemplo no es obligatoria y se podía haber elegido otra: una matriz de 31 x 12 donde las filas corresponderían a los días y las columnas a los meses. 9. El hecho de que la tabla sea una estructura estática no representa ningún problema si el número de elementos que se van a necesitar está predeterminado.
__________________________________________________________________________________________________________ Estructuras Estáticas 4
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
Ejemplo:
Para almacenar las ventas mensuales durante un año se necesitará siempre un vector de 12 elementos: cada uno de los elementos almacenará la venta realizada en un determinado mes.
Pero habrá muchas ocasiones en que no se conoce de antemano el número de elementos necesario; en este caso se deberá prever una tabla con un número suficiente de elementos, tendiendo siempre a sobredimensionar. Ejemplo:
Un programa necesita guardar en un vector los nombres de los distintos autores de los libros de que dispone una biblioteca de 1.000 volúmenes. No se sabe el número de autores distintos: puede que cada libro sea de un autor distinto o puede que sólo haya 100 autores distintos cada uno de los cuales haya escrito 10 de los libros que hay en esa biblioteca. Por ello, suponiendo que se de el caso más desfavorable, habrá que declarar un vector de 1.000 elementos de tipo cadena. Obsérvese que si se da el otro caso descrito se utilizará sólo el 10 % del tamaño del vector. De todos modos, este tamaño del vector servirá sólo si el programa se aplica a esa biblioteca; es decir, para otra biblioteca distinta, con un número distinto de volúmenes, habría que modificar el programa fuente para adecuar ese valor máximo. Es conveniente pues declarar el valor que indica el tamaño de la tabla como una constante de modo que sólo haya cambiar un valor.
10. Hay que tener en cuenta que, al sobredimensionar, se está desperdiciando memoria, sobre todo, si habitualmente no se utilizan todos los elementos de la tabla. Ejemplo:
Un programa debe almacenar en una tabla los nombres de las asignaturas que imparte cada profesor de una ciudad y el curso a quien lo imparte. Supongamos que hay, como mucho, 1000 profesores y cada uno puede tener asignadas un máximo de 10 asignaturas distintas. Se necesitará, por tanto, una tabla bidimensional (matriz) de tipo cadena de 15 caracteres de 1000 filas (una por profesor) y de 10 columnas. Si el número medio de asignaturas distintas asignadas a cada profesor es 5, realmente se utilizará el 50 % del espacio ocupado en memoria por la tabla; es decir, se desperdician un total de 5 * 1000 * 15 bytes = 75 Kb, aproximadamente. Seguramente la estructura elegida no es la más adecuada.
11. Las operaciones que se suelen realizar habitualmente sobre una tabla son: • •
Recorrido: Búsqueda: valor.
Procesamiento de cada elemento de la tabla. Obtener la posición ocupada por un elemento con un determinado
__________________________________________________________________________________________________________ Estructuras Estáticas 5
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
•
Ordenación: Organizar los elementos de la tabla de acuerdo con algún criterio. Solo se realiza sobre tablas unidimensionales: vectores.
También se pueden insertar y/o borrar elementos de la tabla, pero sólo de una forma lógica, nunca física. Puesto que las tablas son habitualmente fáciles de recorrer y de buscar en ellas y de ordenar, suelen utilizarse para almacenar conjuntos de datos relativamente fijos.
3. TABLAS UNIDIMENSIONALES:VECTORES. •
Concepto
Los vectores son tablas de una sola dimensión, es decir, cada elemento del vector se referencia mediante el nombre de la tabla y un solo índice. Los índices son números enteros y consecutivos que indican la posición relativa de cada uno de los elementos dentro del vector. El valor del primer índice (en el lenguaje C) es el 0 y no se puede variar. El identificador de cada elemento (nombre e índice) determina la posición de memoria donde se encuentra almacenado su contenido o valor. El vector se denomina tabla unidimensional porque tiene un sólo índice de referencia, es decir, una sola dimensión. •
Definición Para definir un vector es necesario indicar: -
Clase de almacenamiento Tipo de cada uno de los elementos Identificador del vector Número de elementos (entre corchetes, en C).
El formato es: clase tipo identificador[num_elementos] Ejemplo: int meses[12]; Se está definiendo meses[] como un vector de tipo entero con 12 elementos. Para referenciar cada uno de ellos se usarán los índices desde 0 hasta 11. Es importante, pues, recordar que el 1º elemento de un vector corresponde al índice 0. La definición de un vector sirve, evidentemente, para indicar al compilador la cantidad de memoria que tiene que reservar para los datos que vaya a contener. El compilador lo calcula multiplicando el número de elementos por el espacio que ocupa cada uno que dependerá del tipo del vector. __________________________________________________________________________________________________________ Estructuras Estáticas 6
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
Pueden inicializarse tanto vectores globales (externos) como locales, sean estáticos o no. Las tablas estáticas y las externas son inicializadas a 0 por defecto; o sea, si no se les da ningún valor todos los elementos valdrán 0. Para inicializarlos se coloca la lista de valores entre llaves y separados por comas. Ejemplo: short int meses[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
Cuando el número de valores es menor que el tamaño del vector, el resto de los elementos no inicializados lo hacen a 0. En cambio, si el número de valores es mayor que el tamaño declarado se produce un error. Si se emplean corchetes vacíos cuando se inicializa un vector, el compilador cuenta el número de valores de la lista, y ese será su tamaño. short int meses[] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
Por otra parte, el operador sizeof proporciona el tamaño en bytes del vector. Por ejemplo: tam_array = sizeof meses; //asignaría 24, ya que cada entero corto ocupa 2 bytes
Luego, para obtener el número de elementos de cualquier vector se usará en general: sizeof
identif_array
/ (sizeof
(tipo_array))
#include <stdio.h> #include short int meses[]={31,28,31,30,31,30,31,31,30,31,30,31}; void main(void) { int indice; extern short int meses[]; //declaracion opcional for(indice=0; indice < sizeof meses/(sizeof(short int));indice++) printf ("Mes %2d = %d dias.\n", indice+1, meses[indice]); getch(); }
En C no se comprueban los límites de las tablas: Cuando en un programa queremos hacer referencia a un elemento de una tabla tenemos que colocar una expresión entre los corchetes; pues bien, se supone que el programador habrá colocado una expresión tal que no pueda tomar valores fuera del rango definido. El compilador no se va a entretener en comprobar que el valor que toma esa expresión está dentro de esos límites. •
Paso de vectores a funciones.
Cuando se quiere pasar una tabla unidimensional a una función hay que pasarle la dirección del primer elemento pues en C no se puede pasar el vector completo como argumento a una función. El parámetro actual puede ser el nombre del vector o la dirección de memoria del primer elemento. El parámetro formal se puede declarar como un vector no delimitado (aunque también puede ser el puntero constante __________________________________________________________________________________________________________ Estructuras Estáticas 7
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
correspondiente al nombre del vector –que estudiaremos más adelante- o un vector delimitado). Las tres formas de hacerlo producen igual resultado, es decir, indican a la función que se va a recibir un puntero al primer elemento de un vector. A continuación se implementa una función que halla una media aritmética: #include <stdio.h> #include #define NUM_ALUMNOS 10 float fmedia2 (float vector[] , int); float fmedia (float vector[] , int); void main(void) { float notas[NUM_ALUMNOS], nota_media; int i; for (i=0; i < NUM_ALUMNOS; i++ ) { printf("NOTA %d : ",i+1); scanf("%f", ¬as[i]); // scanf necesita la dirección } nota_media = fmedia (¬as[0], NUM_ALUMNOS); //Tb. nota_media=fmedia(notas,NUM_ALUMNOS); printf("Nota media = %5.2f ", nota_media ); getch(); } /* fmedia es una función general que halla la media de los elementos de cualquier vector de tipo entero. También es válido: float fmedia (float vector[10], int num_elem )*/ float fmedia ( float vector[] , int num_elem ) { float totnotas; int indice; for (indice=0, totnotas=0.0; indice
El vector notas[] es un vector de reales, local a la función principal y cuyo tamaño es de 10 elementos. Sin embargo, el vector vector[] NO EXISTE. La llamada a la función fmedia le pasa como argumento un puntero -una dirección-: notas, o lo que es lo mismo, ¬as[0]. Por tanto, todas las operaciones que afectan a vector[] están trabajando realmente sobre el vector notas[]. Hay que tener en cuenta que la función fmedia no tiene forma de conocer el tamaño del vector que se le ha pasado como parámetro. Luego la llamada a la función fmedia tiene también que incluir como argumento el tamaño del vector. Mejorando la función fmedia anterior..... // continúa el Ejemplo anterior */ float fmedia2 ( float vector[] , int num_elem ) { int indice; float totnotas; for (indice=0, totnotas = 0.0 ; indice < num_elem ; indice++) totnotas+=vector[indice]; if (num_elem) return ( totnotas / num_elem ) ; else return 0.0; __________________________________________________________________________________________________________ Estructuras Estáticas 8
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
}
Comunmente, en C se realiza el tratamiento de tablas mediante punteros, ya que, como se ha mencionado anteriormente, el nombre de la tabla es, en realidad, un puntero al primer elemento de la tabla. Con el fin de no confundir el uso de tablas con el uso de punteros (algo más engorroso), por ahora sólo hemos usado las tablas de la forma convencional. Al final de este tema, y cuando el alumno maneje sin dificultad las tablas, se hará un extenso estudio del uso de punteros con ellas. Nota:
4. BÚSQUEDA Y ORDENACIÓN INTERNAS. Una de las operaciones más utilizadas con colecciones de datos es la búsqueda de un dato concreto de entre todos ellos. La eficiencia de una búsqueda dependerá enormemente del grado de ordenación previa de la que dispongan los datos o de la idea que podamos tener sobre la ubicación de la información a buscar; no es lo mismo encontrar un libro en una biblioteca bien organizada que en un almacén de libros sin clasificar. En cada caso debemos encontrar el método que mejor se adapte a nuestras necesidades, sin embargo, existen una serie de algoritmos comunes tanto para la búsqueda de un dato como para la ordenación de una colección de ellos. No hay que desarrollar un algoritmo que funcione, hay que buscar el que lo haga de la mejor forma posible. Entre los métodos de búsqueda estudiaremos los más clásicos: Los algoritmos de búsqueda pueden ser: - Para datos no ordenados: Búsqueda lineal - Para datos ordenados. Búsqueda lineal Búsqueda binaria o dicotómica Los métodos de ordenación se pueden dividir, en una primera aproximación, en dos tipos: - Ordenación interna.- Ordenación externa.-
La que tiene lugar en la memoria del ordenador utilizando estructuras tipo tabla. Se realiza sobre elementos (registros de ficheros de datos) almacenados en un dispositivo externo (no en memoria), como pueden ser discos duros, disquetes, etc.
Nosotros abordaremos en este tema tres métodos de ordenación interna sobre vectores de elementos (aunque pueden aplicarse perfectamente sobre estructuras de datos más complejas). La eficiencia de un método de ordenación suele determinarse en base a dos factores: el número de comparaciones, C, y el número de movimientos de las mismas, M. Ambos son función del número N de elementos que componen el vector a ordenar. Una clasificación de estos algoritmos puede ser la siguiente:
__________________________________________________________________________________________________________ Estructuras Estáticas 9
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
- Métodos directos:
Suelen ser algoritmos cortos y fáciles de entender, aunque algo lentos. De entre ellos se estudiarán los señalados en negrita: - Método de inserción directa - Método de selección directa - Métodos de intercambio - burbuja - burbuja con switch - sacudida - con incrementos decrecientes. Método Shell.
- Métodos Avanzados:
Mucho más complejos que los anteriores. Entre ellos encontramos el método QuickSort (qsort()), basado en un algoritmo recursivo (técnica de programación que permite la llamada de funciones a sí mismas), que veremos en temas posteriores.
Todos los métodos relacionados aquí se utilizan con estructuras de datos internas unidimensionales (tablas), es decir, no se utilizan para el caso de ficheros en disco. Además, por claridad se utilizan datos numéricos en las tablas, lo cual no quiere decir que no pueda hacerse con cualquier otro tipo de datos donde pueda establecerse una ley de ordenación (caracteres, cadenas, etc). Se considera que en los vectores no hay valores repetidos (considérense las diferencias que puedan haber en cada caso si no se cumple esta premisa). •
Algoritmos de búsqueda: Búsqueda lineal.
Las siguientes declaraciones las utilizaremos para la elaboración de todos los algoritmos aquí descritos: #define N 10 int v[N]; siendo N el número de elementos y v el vector. Todos los algoritmos están expresados en forma de función, que devuelve el índice del elemento a buscar o –1 en caso de que el elemento no se encuentre en el vector. Intuitivamente, el algoritmo de búsqueda lineal es el siguiente: “Se recorre el vector desde el primer elemento al último hasta encontrar un elemento cuyo valor coincida con el buscado o hasta que se acabe el vector. En este último caso, el algoritmo debe indicar la no existencia de dicho valor en el vector” int Busqueda_Lineal_Desordenado(int v[],int elementos, valor_a_buscar) { int i; for (i=0;v[i]!=valor_a_buscar && i<elementos-1;i++); if (v[i]==valor_a_buscar) return (i);
int
__________________________________________________________________________________________________________ Estructuras Estáticas 10
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
else return -1; }
Para llamar a la función, y como ejemplo, podríamos usar el siguiente programa: //Búsqueda Lineal en vectores desordenados #include <stdio.h> #define N 10 int Busqueda_Lineal_Desordenado(int v[],int elementos, valor_a_buscar) { int i; for (i=0;v[i]!=valor_a_buscar && i<elementos-1;i++); if (v[i]==valor_a_buscar) return (i); else return -1; } main() { int v[N]={22,14,88,11,6,15,4,33,52,1}; int valor = 14; int posicion=Busqueda_Lineal_Desordenado(v,N,valor); if (posicion>-1) printf("Encontrado en la posicion %d",posicion); else printf("No encontrado"); getchar(); }
int
Cuando el vector de búsqueda está ordenado se consigue un algoritmo más eficiente con sólo modificar la condición de terminación de la instrucción for: int Busqueda_Lineal_Ord_Ascen(int v[],int elementos, valor_a_buscar) { int i; for (i=0;v[i]
int
La ventaja que se obtiene es que, una vez sobrepasado el valor buscado, no es necesario recorrer el resto del vector para saber que el valor no existe. En caso de haber valores repetidos, se encontrará el valor situado en primer lugar según el orden ascendente del vector. Sin embargo, para realizar una búsqueda sobre un vector ya ordenado es mucho más ventajoso utilizar el algoritmo de búsqueda binaria o dicotómica pues el número de intentos para encontrar un valor dado es drásticamente más bajo con la consecuente mejora en velocidad, si bien, en caso de que el vector sea pequeño (10 elementos o menos) una búsqueda lineal no es un mal recurso. •
Algoritmos de búsqueda: Búsqueda binaria o dicotómica.
__________________________________________________________________________________________________________ Estructuras Estáticas 11
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
Este algoritmo es válido sólo y exclusivamente para vectores ordenados y consiste en “comparar en primer lugar con el componente central del vector que, si no es igual al valor buscado, se reduce el intervalo de búsqueda a la mitad derecha o izquierda, según donde pueda encontrarse el valor a buscar. El algoritmo termina si se encuentra el valor buscado o si el tamaño del intervalo de búsqueda queda anulado”. En los casos en que existan repeticiones en el vector, del valor buscado, este algoritmo obtendrá uno de ellos aleatoriamente según los lugares que ocupen, los cuales necesariamente son consecutivos. Se trata de una búsqueda similar a la de una palabra en un diccionario, donde aprovechamos el orden alfabético para dirigirnos con rapidez al lugar donde puede estar la palabra buscada. Para su realización utilizamos tres variables que nos indican en qué zona del vector nos encontramos. Son estas variables: int izq, der, cen; El vector v se supone ordenado ascendentemente: int Busqueda_Dicotomica(int v[],int elementos, int valor_a_buscar) { int izq=0, der=elementos-1, cen=(izq+der)/2; while (v[cen]!=valor_a_buscar && izq<der) { if (v[cen]>valor_a_buscar) der=cen-1; else izq=cen+1; cen=(izq+der)/2; } if (v[cen]==valor_a_buscar) return cen; else return -1; }
•
Ordenación por inserción: Inserción directa
También llamado método de la baraja. El método consiste en “tomar los elementos del vector desde el segundo hasta el último y con cada uno de ellos repetir el siguiente conjunto de operaciones: 1.- Se saca del vector el elemento I-ésimo (aux es una variable que lo recibe). 2.- Desde el anterior al que estamos tratando y hasta el primero, desplazamos un lugar a la derecha todos los que sean mayores para buscar su hueco. 3.- Encontrado el hueco del elemento, lo insertamos en él. __________________________________________________________________________________________________________ Estructuras Estáticas 12
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
Conviene observar que, cuando tratamos al elemento i-ésimo, todos los anteriores se encuentran ordenados.” - aux es una variable auxiliar del mismo tipo que los elementos del vector que contendrá el valor del elemento i-ésimo que vamos a tratar. - i apunta al elemento que vamos a tratar. - j apunta a los elementos anteriores. void Ordena_Insercion(int v[], int elem) { int i,j,aux; for (i=1;i<elem;i++) { aux=v[i]; j=i-1; while (v[j]>aux && j>0) { v[j+1]=v[j]; j--; } if (v[j]>aux) { v[j+1]=v[j]; v[j]=aux; } else v[j+1]=aux; } }
•
Ordenación por intercambio: Intercambio directo o “burbuja”
Este método tiene dos versiones basadas en la misma idea, que consiste en “recorrer sucesivamente el vector comparando los elementos consecutivos e intercambiándolos cuando estén descolocados”. El recorrido del vector se puede hacer de izquierda a derecha (desplazando los valores mayores hacia su derecha) o de derecha a izquierda (desplazando los valores menores hacia su izquierda), ambos para la clasificación en orden ascendente. A continuación veremos el método con recorrido izquierda-derecha. Consiste en realizar pasadas sucesivas direccionando desde el primer elemento hasta el penúltimo, comparando cada uno de ellos con el siguiente. Esta versión del método va colocando en cada pasada el menor elemento de los tratados en la primera posición, quedando colocado y por tanto excluido de los elementos a tratar en la siguiente pasada: - i cuenta el número de pasadas. - j apunta al elemento a tratar. - aux se utiliza para los intercambios. void Ordena_Burbuja(int v[], int elem) { int i,j,aux; for (i=0;i<elem-1;i++) __________________________________________________________________________________________________________ Estructuras Estáticas 13
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
for (j=elem-1;j>i;j--) if (v[j]
}
•
Ordenación por intercambio: Intercambio directo mejorado o “burbujaswitch”
Es una mejora de los métodos de intercambio directo en la que se comprueba mediante un switch (interruptor o bandera) si el vector está totalmente ordenado después de cada pasada, terminando la ejecución en caso afirmativo. La comprobación de la ordenación en cada pasada consiste en detectar si se han producido intercambios o no, de tal forma que sólo se realizarán las pasadas necesarias que dependerán del grado de desorden existente en los datos iniciales. void Ordena_Burbuja_Switch(int v[], int elem) { int i,j,aux,sw; i=0; sw=1; while (i<elem-1 && sw) { sw=0; for (j=elem-1;j>i;j--) if (v[j]
•
Ordenación por intercambio: Intercambio con incrementos decrecientes o “Shell”
El método burbuja acerca cada elemento a la posición que le corresponde paso a paso. Su tiempo de ejecución para vectores pequeños puede ser aceptable. Un algoritmo mucho más eficaz y más apropiado para la ordenación de grandes vectores es la ordenación Shell, denominado así en honor a su inventor. Consiste en comparar, no elementos consecutivos como lo hacía el de la burbuja, sino los que están separados por un intervalo grande, de modo que cada uno se acerca al lugar que le corresponde de forma más rápida. Inicialmente, ese intervalo o salto corresponde con la mitad de la longitud del vector. Posteriormente, cuando ya todos los elementos separados por esa distancia están ordenados, se divide ese intervalo por dos y se opera de igual forma hasta que, finalmente, el salto se reduzca a 1. Entonces, el proceso de __________________________________________________________________________________________________________ Estructuras Estáticas 14
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
ordenación funciona exactamente como el de la burbuja: comparando elementos adyacentes. Los elementos que vamos a utilizar dentro de nuestro algoritmo son los siguientes: d es una variable que indica la distancia de comparación. sw comprueba la ordenación a distancia d. 1 = desordenado a distancia d. 0 = ordenado a distancia d. void Ordena_Shell(int v[], int elem) { int i,d,sw,aux; d=elem; while (d!=1) { d/=2; sw=1; while (sw) { sw=0; for (i=0;i<elem-d;i++) if (v[i]>v[i+d]) { aux=v[i]; v[i]=v[i+d]; v[i+d]=aux; sw=1; } } } }
EJERCICIO.Como ejercicio de tratamiento de vectores, el alumno debe implementar un algoritmo de ordenación que no se ha visto anteriormente: la ordenación por Selección Directa. A continuación se expresa con palabras su funcionamiento: El método consiste en “repetir el siguiente proceso desde el primer elemento hasta el penúltimo: se selecciona el componente de menor valor de todos los situados a la derecha del tratado y se intercambia con éste”. En realidad se trata de sucesivas búsquedas del menor de los elementos que quedan por ordenar. Para la realización del intercambio se utiliza una variable auxiliar aux.
5. CADENAS. El lenguaje C no dispone de un tipo especial para cadenas, sino que las cadenas son vectores de caracteres (de tipo char) que terminan con el carácter nulo, o sea, ‘\0‘ . De todos modos, el lenguaje C soporta la mayoría de las funciones más potentes de manipulación de cadenas que aparecen en cualquier lenguaje. Existen muchas formas diferentes de definir en C una cadena o tira. Veámoslas. __________________________________________________________________________________________________________ Estructuras Estáticas 15
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
•
Cadenas constantes
Cualquier conjunto de caracteres encerrados entre comillas es una cadena constante. Los caracteres que estén encerrados entre las comillas, junto con un carácter '\0', se almacenan en posiciones adyacentes de memoria. No es necesario añadir de forma manual el carácter nulo al final de las constantes de cadena: el compilador de C lo hace de forma automática; de igual modo, cuenta el número de caracteres de la cadena de forma que conoce de antemano cuánta memoria va a necesitar para su almacenamiento. El modo de almacenamiento es static. Se utilizan cadenas constantes como argumentos de las funciones puts(), printf(), etc.. Nota: La función puts() muestra en pantalla la cadena que se le pasa como argumento seguida de un carácter de salto de línea. También las constantes cadena pueden ser asignadas a macros: Ejemplo: #define VOCALES "aeiou" ......... puts(VOCALES); puts("Esta es una tira"); printf(“%s\n%s\n”, VOCALES, "Esta es otra tira"); La frase entera entrecomillada actúa como puntero al lugar donde se ha almacenado la misma. Ejemplo: printf("%s ,%u, %c\n","Domínguez","Delgado",*"a tí dedicado");
dará como resultado: Domínguez,
4239842, a
Al representar la cadena "Delgado" como entero sin signo obtenemos la dirección a partir de la cual está almacenada. Por otra parte, si aplicamos el operador de indirección (*) a la 3ª cadena obtenemos el contenido de la dirección donde apunta dicha cadena, o sea, el primer carácter de la propia cadena. •
Vectores de caracteres. Para inicializar un vector de caracteres se escribe...
char tira1[]= {'L','e','n','g','u','a','j','e',' ','C','\0'}; static char tira2[]= {'L','e','n','g','u','a','j','e',' ','B','\0'}; extern char tira3[]= {'L','e','n','g','.',' ','C',’+’,’+’,'\0'};
Observe el carácter nulo al final de la cadena. Sin embargo, existe una forma más corta de inicialización: char tira1[] = "Lenguaje C"; static char tira2[] = "Lenguaje B"; __________________________________________________________________________________________________________ Estructuras Estáticas 16
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
extern char tira3[]
= "Leng. C++”;
Al igual que en otro tipo de vectores, tampoco aquí es necesario indicar el tamaño del vector puesto que el compilador cuenta los caracteres y prepara el tamaño correspondiente. Si al declarar un vector de caracteres se especifica su tamaño, hay que tomar la precaución de que el número de elementos declarados sea como mínimo superior en 1 (el correspondiente al carácter nulo) a la longitud de la cadena con que se inicializa. Si el tamaño es inferior a dicho mínimo se producirá un error; si es superior, los elementos sobrantes se inicializan a '\0'. Habrá casos en que se declare un vector y no se inicialice, por ejemplo: char nombre[81]; donde nombre es un vector para ser leído en tiempo ejecución. Aquí se tiene que declarar obligatoriamente el tamaño, y se deberá controlar la entrada de caracteres para que su número no sobrepase el espacio reservado (el tamaño menos 1). En el ejemplo anterior, nombre puede almacenar hasta 80 caracteres como máximo más el carácter nulo; en total, 81 caracteres.
6. FUNCIONES DE E/S PARA CADENAS. Anteriormente hemos estudiado y usado las funciones scanf() y printf(), que permiten la entrada y la salida, respectivamente, de diferentes tipos de datos, entre ellos, de las cadenas. Ahora estudiaremos la funciones gets() y puts() cuya misión es, respectivamente, leer y escribir cadenas de caracteres. •
Función gets()
La función gets() captura cadenas desde el teclado; la entrada termina al pulsar INTRO. Los errores cometidos al teclear la cadena pueden ser borrados utilizando la tecla de retroceso antes de pulsar la tecla Intro. La cadena es colocada en la dirección apuntada por su argumento, un puntero a carácter. El último carácter pulsado, o sea, el retorno de carro ( ‘\r’ ) no pasa a formar parte de la cadena, siendo sustituido por el carácter nulo ( ‘\0’ ). A diferencia de scanf(), la función gets() sí permite que se introduzcan espacios en blanco al entrar la cadena. Su prototipo es: char *gets (char *cadena) ; Como se puede apreciar, esta función devuelve un puntero a la cadena leída. Un problema importante que puede aparecer al usar esta función es sobrepasar los límites de la tabla que se le pasa como argumento, ya que la función no tiene forma de saber cuando se alcanzan los limites de la tabla. Si, por ejemplo, se introducen 21 caracteres (contando el nulo) cuando sólo se han reservado para la cadena 20, se producirán errores, incluso la caída del sistema. Como alternativa, y para evitar este tipo de problemas, se puede usar la función fgets() que se estudiará más adelante. __________________________________________________________________________________________________________ Estructuras Estáticas 17
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
•
Función puts()
La función puts() da salida sólo a cadenas y por ello es más simple que printf() por lo que deberemos emplearla siempre que sea posible pues así se añade menos código a nuestro programa y se ejecuta más rápidamente que si se emplea esta última. Hay que tener en cuenta que puts() añade automáticamente un retorno de carro, lo que no hace printf() Su prototipo es: int
puts (char * cadena) ;
Esta función devuelve 0 si se ejecuta correctamente y otro valor en caso contrario. #include <stdio.h> #include void main (void) { char nom_mes[16], nom_dia[21], nom_ape[51]; puts("Introduzca cada dato seguido de INTRO"); puts("(si se permiten espacios)"); puts(""); puts("Nombre y apellidos (max. 50 car.): "); gets(nom_ape); puts("Nombre de un mes (max. 15 car.): "); gets(nom_mes); puts("Nombre de un día de la semana (max. 20 car.): "); gets(nom_dia); printf("\n\n"); puts(nom_ape); puts("encuentre los días de este año que caen en ..."); puts (nom_dia); puts ("y pertenecen al mes..."); puts (nom_mes); getch(); }
7. FUNCIONES PARA TRATAMIENTO DE CADENAS. Las siguientes funciones utilizan el archivo de cabecera string.h donde se encuentra las declaraciones de sus prototipos. •
Función strcat() char *strcat (char *cad1, const char *cad2);
Esta función concatena una copia de cad2 al final de cad1 y añade al final de cad1 un carácter nulo. El carácter nulo que inicialmente finalizaba la cadena cad1 es sobrescrito por el primer carácter de cad2. La cadena cad2 no se modifica con esta operación. Esta función devuelve cad1. Resumiendo, al final en cad1 queda: cad1 + cad2. __________________________________________________________________________________________________________ Estructuras Estáticas 18
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
Téngase en cuenta que no se realizan comprobaciones de límites y, por tanto, es responsabilidad del programador asegurar que cad1 es lo suficientemente grande como para almacenar su contenido original y el contenido de cad2. •
Función strcmp() int strcmp (const char *cad1, const char *cad2);
Esta función compara de forma lexicográfica (tiene en cuenta el orden establecido por la tabla ASCII) dos cadenas que finalizan con un carácter nulo y devuelve un entero que se interpreta así: Menor que 0 cad1 es menor que cad2 0 cad1 es igual que cad2 Mayor que 0 cad1 es mayor que cad2 •
Función strcpy() char *strcpy (char *cad1, const char *cad2);
Esta función se utiliza para copiar el contenido de cad2 en cad1; cad2 debe ser un puntero a una cadena que termine en un carácter nulo. Además, devuelve un puntero a cad1. Si cad1 y cad2 se solapan, el comportamiento de la función es indefinido. •
Función strncat()
char *str ncat(char* cad1,const char* cad2,size_t cuenta);
El tipo size_t está definido, mediante typedef, como un entero sin signo. Esta función concatena, a la cadena apuntada por cad1, un máximo cuenta caracteres de la cadena apuntada por cad2 y coloca al final de cad1 un carácter nulo. El carácter nulo de fin de cadena que, inicialmente, finalizaba cadena cad1 se sustituye por el primer carácter de cad2. Esta función no modifica la cadena cad2. La función strncat devuelve cad1. Recuérdese que no se realizan comprobaciones de límites y, por tanto, es responsabilidad del programador asegurar que cad1 tiene el tamaño suficiente almacenar su contenido original y el de cad2. •
Función strncpy() char *strncpy(char *dest,const char *fuente,size_t cuenta);
Esta función se utiliza para copiar un máximo de cuenta caracteres de la cadena apuntada por fuente a la cadena apuntada por dest. La cadena fuente debe ser un puntero a una cadena terminada con un carácter nulo. La función strncpy devuelve un puntero a dest. __________________________________________________________________________________________________________ Estructuras Estáticas 19
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
Cuando se solapan las cadenas dest y fuente, el comportamiento de la función strncpy es indefinido. Si la cadena apuntada por fuente tiene un número de caracteres menor que cuenta, se añaden caracteres nulos al final de dest hasta que se hayan copiado los cuenta caracteres. Alternativamente, cuando la cadena apuntada por fuente tiene un número de caracteres superior a cuenta, se copian cuenta caracteres al principio de dest; evidentemente, a continuación del último copiado están los caracteres que contenía inicialmente la cadena fuente. •
Función strnset() char *str nset (char *cad, int c, size_t cuenta);
Esta función define los primeros cuenta caracteres de la cadena apuntada por cad con el valor de c. Esta función devuelve cad. •
Función strstr() char *strstr (const char *cad1, const char *cad2);
Esta función devuelve un puntero a la primera ocurrencia de la cadena apuntada por cad2 (sin considerar el carácter nulo de fin de cadena) dentro de la cadena apuntada por cad1. Devuelve un puntero nulo si no encuentra ninguna coincidencia.
8. MACROS Y CARACTERES.
FUNCIONES
PARA
TRATAMIENTO
DE
Las siguientes macros, y también las funciones, que se describen a continuación, necesitan incluir el archivo de cabecera ctype.h. En éste se encuentra las declaraciones de sus prototipos. •
Macro isalnum ( ) int isalnum (int c);
Devuelve un valor distinto de 0 si su argumento es una letra del alfabeto (mayúscula o minúscula) o un dígito; en caso contrario, o sea, si el carácter no es alfanumérico devuelve 0. •
Macro isalpha ( )
int isalpha (int c); Devuelve un valor distinto de 0 si su argumento es una letra del alfabeto (mayúscula o minúscula); en caso contrario, o sea, si el carácter no es una letra devuelve 0. •
Macro isdigit ( )
__________________________________________________________________________________________________________ Estructuras Estáticas 20
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
int isdigit (int c); Devuelve un valor distinto de 0 si su argumento es un dígito (un valor del ‘0' al ‘9'); en caso contrario, devuelve 0. •
Macro islower ( ) int islower (int c);
Devuelve un valor distinto de 0 si su argumento es una letra minúscula (de la «a» a la «z»); en caso contrario, devuelve 0. •
Macro ispunct ( ) int ispunct (int c);
Devuelve un valor distinto de 0 si su argumento es un carácter de puntuación o un espacio en blanco; en caso contrario, devuelve 0. •
Macro isspace ( ) int isspace (int c);
Devuelve un valor distinto de 0 si su argumento es un espacio en blanco, un retorno de carro, un tabulador horizontal, un tabulador vertical, un carácter de salto de línea o un carácter de nueva línea; en caso contrario, devuelve 0. •
Macro isupper ( ) int isupper (int c);
Devuelve un valor distinto de 0 si su argumento es una letra mayúscula (de la «A» a la «Z»); en caso contrario, devuelve 0. •
Función tolower ( ) int tolower (int c);
Si el argumento «c» es un letra mayúscula, esta función devuelve la letra minúscula equivalente a «c»; en cualquier otro caso, devuelve «c» sin modificar. •
Función toupper ( ) int toupper (int c);
Si el argumento «c» es un letra minúscula, esta función devuelve la letra mayúscula equivalente a «c»; en cualquier otro caso, devuelve «c» sin modificar. __________________________________________________________________________________________________________ Estructuras Estáticas 21
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
9. TABLAS BIDIMENSIONALES: MATRICES. •
Definición. Inicialización. Acceso.
Las tablas bidimensionales o matrices se declaran utilizando el formato general: tipo nombre_array[tamaño_primera_dimension][tamaño_segunda_dimension];
Ejemplo: Para almacenar datos de la lluvia caída cada mes durante 5 años definimos: float lluvia [5][12]; donde el vector principal tendría 5 elementos, que serían a su vez, vectores de 12 elementos. Para inicializar una matriz se colocan los valores de cada fila entre llaves y separados por comas. Los valores de diferentes filas se separan por comas. Todo el conjunto se encierra entre un par de llaves externo. Ejemplo:
La definición...
int puntos[3][4]={{1,9,3,8},{2,6,4,6},{9,0,1,2}}; dará como resultado la siguiente matriz: F I L A S
COLUMNAS 0 0 1 1 2 2 9
1 9 6 0
2 3 4 1
3 8 6 2
Ocurre que si entre un par de llaves hay más elementos de los declarados se producirá un error; pero si hay menos, los elementos restantes se incializarán a 0 por defecto. Ejemplo:
La definición...
int puntos [4][5]={ {1,2} , {5,6,7,2,0} , {9,0,4} }; dará como resultado la siguiente matriz: 0 1 2 3
0 1 5 9 0
1 2 6 0 0
2 0 7 4 0
3 0 2 0 0
4 0 0 0 0
Es posible inicializar la matriz colocando sólo el par de llaves externos. Ejemplo:
La definición equivalente a la anterior es...
int puntos [4] [5] = { 1,2,0,0,0,5,6,7,2,0,9,0,4}; __________________________________________________________________________________________________________ Estructuras Estáticas 22
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
Hay que tener en cuenta que cuando no se colocan llaves internas la tabla bidimensional se va llenando en orden por filas. Ejemplo:
La definición...
int puntos [3] [4] = {1,2,5,6,7,2}; dará como resultado la siguiente matriz: 0 1 7 0
0 1 2
1 2 2 0
2 5 0 0
3 6 0 0
#include <stdio.h> #include #define MAX_ALUMNOS 15 #define MAX_ASIGNATURAS void main(void) {
6
int i, j, notas[MAX_ALUMNOS][MAX_ASIGNATURAS]={{6,7,6},{5,3,2},{7,5,4,2,3}};
printf("ALUMNO RAL FP SMM IEA\n\n"); for (i = 0 ; i < MAX_ALUMNOS ; i++) { printf("\n%4d) ", i+1); //numero del alumno for (j = 0 ; j < MAX_ASIGNATURAS ; j++) printf("%8d", notas[i][j]); } getch(); }
•
FOL
REL
Almacenamiento en memoria de tablas multidimensionales
En la representación gráfica anterior hemos podido observar la forma en que las tablas bidimensionales se almacenan en memoria: ordenadamente por filas, es decir, que cuando se accede a sus elementos de forma consecutiva, el índice de más a la derecha cambia más rápido que el de la izquierda. Lo mismo se puede decir para las tablas de más dimensiones. Por ejemplo: para una tabla tridimensional trid [3][5][2] el orden en que se guardarían en memoria sus elementos sería... trid [0][0][0] trid [0][0][1] trid [0][1][0] trid [0][1][1] trid [0][2][0] trid [0][2][1]...
•
Paso de tablas bidimensionales a funciones
Cuando se utiliza una tabla bidimensional como argumento de una función, se pasa un puntero al primer elemento del mismo. Sin embargo, una función que recibe como argumento una tabla bidimensional debe definir como mínimo la longitud de la segunda dimensión, debido a que el compilador necesita conocer la longitud de cada fila para indexar la tabla correctamente: Si no se conoce la longitud de las filas, es imposible saber dónde comienza la siguiente fila. También se puede especificar la primera dimensión, pero no es necesario. __________________________________________________________________________________________________________ Estructuras Estáticas 23
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
#include <stdio.h> #include #include #define ASIGNAT 3 #define ALUMNOS 5 void ver_notas(int n[][ALUMNOS]); void leer_notas(int n[][ALUMNOS]); void main(void) { char ch; int notas[ASIGNAT][ALUMNOS]={0}; do { do { clrscr(); printf("(E)ntrar notas\n"); printf("(M)ostrar notas\n"); printf("(S)alir\n"); ch = toupper(getch()); } while(ch!='E' && ch!='M' && ch!='S'); clrscr(); switch(ch) { case 'E': leer_notas(notas); break; case 'M': ver_notas(notas); break; } } while (ch != 'S'); } //***************************************************** void leer_notas(int n[][ALUMNOS]) { int asig, i, nota; for(asig=0; asig < ASIGNAT; asig++) { printf("\n\nAsignatura %d: \n", asig + 1); for(i=0; i< ALUMNOS; ++i) { do { printf("\nAlumno %d : ", i+1); nota = getche(); } while (nota <'0' || nota >'9'); n[asig][i] = nota -'0'; //Equivale scanf("%d", n[asig]+i); } } } //***************************************************** void ver_notas (int g[][ALUMNOS]) { int asig, i; for(asig=0; asig < ASIGNAT; ++asig) { printf("\n\nAsignatura %d: \n", asig + 1); for(i=0; i < ALUMNOS; ++i) printf("%d) %d ",i+1, g[asig][i]); __________________________________________________________________________________________________________ Estructuras Estáticas 24
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
} getch(); }
10. TABLAS TRIDIMENSIONALES: POLIEDROS. Las tablas tridimensionales se declaran utilizando el formato general: tipo nombre_array [tamaño_1ra_dim] [tamaño_2da_dim] [tamaño_3ra_dim]; #define FILA 3 #define COLU 4 #define PAGI 2 void ver_array( int [][COLU][PAGI], int , int , int ); void main(void) { int tridi[FILA][COLU][PAGI]={{{9,10},{11,12},{13,14},{15,16}}, {{17,18},{19,20},{21,22},{23,24}}, {{25,26},{27,28},{29,30},{31,32}}}; int f, c, p ; for (p=0; p< PAGI; p++) //Muestra el poliedro por páginas { printf("PAGINA %d\n", p); for (f=0; f < FILA; f++) { for (c=0; c < COLU ; c++ ) printf("%5d", tridi[f][c] [p]); printf("\n"); } } for (p=0; p< PAGI; p++) //los bucles se pueden poner en for (f=0; f< FILA; f++) // cualquier orden for (c=0; c < COLU ; c++ ) tridi[f][c][p] += 5 ; //Suma 5 a cada elemento printf("\n\nEl array modificado es...\n"); ver_array (tridi, FILA, COLU, PAGI); getch(); } //***************************************** void ver_array(int arr[][COLU][PAGI], int fil, int col, int pag ) { int p,f,c; for (p=0; p< pag; p++) //Muestra el poliedro por páginas { printf("PAGINA %d\n", p); for (f=0; f < fil; f++) { for (c=0; c < col ; c++ ) printf("%5d", arr[f][c] [p]); printf("\n"); } } }
Al igual que ocurre con las tablas bidimensionales, cuando se pasa una tabla tridimensional a una función, el argumento efectivo es el nombre de la tabla y en los argumentos formales se puede especificar una tabla en la que hay que especificar obligatoriamente todas las dimensiones, excepto la primera.
__________________________________________________________________________________________________________ Estructuras Estáticas 25
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
11. CONCEPTO Y DEFINICIÓN DE ESTRUCTURAS. Una estructura es un conjunto de variables a las que se hace referencia bajo un mismo nombre, siendo una forma eficaz de mantener agrupada una información relacionada. Las variables que componen la estructura se llaman miembros de la estructura, o también, elementos o campos. Una declaración de estructura forma una plantilla que puede utilizarse para crear estructuras de objetos. La palabra reservada struct indica al compilador que se va a definir una estructura. En general, cada miembro de la estructura está relacionado con el resto. Ejemplo: En una lista de personas la información relativa al nombre, dirección, etc. se representa, normalmente, como una estructura. En el siguiente fragmento de código se muestra cómo declarar una estructura como una plantilla que contenga dichos campos. struct agenda { char nombre[30]; char calle[40]; char ciudad[20]; char provincia[15]; char codigo[5]; char telefono[9]; int importe; }; La declaración termina con un punto y coma ya que la declaración de una estructura es una instrucción. El nombre de la estructura (agenda, en nuestro ejemplo) identifica esta estructura de datos concreta y su identificador de tipo. El nombre de la estructura también se denomina etiqueta. Hasta ahora no se ha creado ninguna variable, sólo se ha definido la forma de los datos. Para declarar una variable con la estructura anterior, se debería escribir... struct agenda clientes_morosos; Esto declara una variable llamada clientes_morosos del tipo de la estructura agenda. Cuando se declara una estructura, se está definiendo un tipo de variable compuesta. Hasta que no se declara una variable de ese tipo, realmente no existe ninguna. Cuando se declara una variable del tipo de estructura, el compilador reserva automáticamente el espacio de memoria necesario para cada uno de sus miembros. Al definir una estructura, también se pueden declarar una o más variables. __________________________________________________________________________________________________________ Estructuras Estáticas 26
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
Ejemplo: struct mini_agenda { char nombre[30]; char telefono[9]; } amigos, clientes, colegas; Es importante tener siempre presente que cuando se crea una variable de un tipo estructura, se mantiene una copia de cada miembro de la misma. Por ejemplo, el campo telefono de clientes está en una zona de memoria distinta y es diferente al campo telefono de amigos. De hecho, la única relación que existe entre amigos y clientes es que ambos son una instancia del mismo tipo de estructura. No existe ninguna otra relación entre ambas. Si sólo se necesita una variable de un tipo de estructura, no es necesario indicar la etiqueta al definir la estructura. Esto significa que struct { char titulo[100]; char director[50]; int precio; } pelicula ; declara una variable llamada pelicula del tipo de estructura que le precede. El formato general de declaración de una estructura es struct etiqueta { tipo nombre_variable; tipo nombre_variable; tipo nombre_variable; ............. } variables_de_estructura ; La etiqueta es un nombre del tipo de la estructura, no un nombre de variable. Por otra parte, variables_de_estructura es una lista de variables separadas por comas. Recuérdese que, aunque la etiqueta y las variables_de_estructura son opcionales, es obligatorio especificar, al menos, una de ellas. •
Referencia a los miembros de una estructura.
Para hacer referencia a cada miembro de una estructura se utiliza el operador . («punto»). Ejemplos: strcpy (cliente_morosos.codigo, “11995"); __________________________________________________________________________________________________________ Estructuras Estáticas 27
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
strcpy (amigos.telefono , “956998877"); pelicula.precio=3000; gets(pelicula.titulo); puts(cliente.nombre); scanf(“%d”, &pelicula.precio); scanf(“%s”, pelicula.director); Un miembro de la estructura se referencia indicando el nombre de variable de la estructura, seguido por un punto y, por último, el nombre del miembro. El formato general es, por tanto: nombre_de_estructura . nombre_de_miembro •
Asignaciones de estructuras.
La información contenida en una estructura se puede asignar a otra del mismo tipo mediante una única instrucción de asignación. Es decir, no es necesario asignar valor a valor cada miembro de la estructura. Ejemplo: colegas = amigos; Esto hace que los miembros de colegas tengan los mismos valores que los miembros de amigos. •
Tablas de estructuras.
Posiblemente, la utilización más común de las estructuras son las tablas de estructuras. Para declarar una tabla de estructuras, se debe definir primero la estructura y, a continuación, declarar una variable tabla de dicho tipo. Por ejemplo, para declarar un vector de 100 elementos del tipo pelicula (declarado anteriormente) se debería hacer de la siguiente forma: struct pelicula peli_cienciaficc [100]; Esto crea 100 variables con la organización de la estructura de tipo pelicula. Para acceder a una determinada estructura del vector peli_cienciaficc, se indexa el nombre de la variable vector. Por ejemplo, para mostrar el precio de la tercera estructura, se puede hacer lo siguiente printf (“%d”, peli_cienciaficc[2].precio); Como ocurre con todas las variables de tipo array, los arrays de estructuras comienzan con índice 0. •
Paso de estructuras a funciones.
__________________________________________________________________________________________________________ Estructuras Estáticas 28
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
Cuando se pasa un miembro de una estructura a una función, lo que realmente se está pasando a la función es el valor de dicho miembro. Por tanto, se está pasando una variable simple. Sin embargo, si se desea pasar la dirección de un miembro de la estructura, para conseguir el paso de un parámetro por referencia, se debe colocar el operador & delante del nombre de la estructura. Ejemplos: fsuma (pelicula.precio); fdescuento (&pelicula.precio); fimprime (pelicula.titulo[i]); fmayusculas (&pelicula.titulo[i]); fquitaespacios (pelicula.titulo);
//pasa el valor de precio //pasa la dirección de precio //pasa el valor de titulo[i] //pasa la dirección de titulo[i] //pasa la dirección de la cadena //titulo
Obsérvese que el operador & precede al nombre de la estructura, no al nombre del miembro. También se puede apreciar que el vector titulo ya indica una dirección, de forma que no es necesario el &. Sin embargo, cuando se accede a un carácter concreto de la cadena titulo, el operador & es necesario. Cuando se utiliza una estructura como argumento de una función, ésta se pasa de forma íntegra mediante el método estándar de llamada por valor. Esto significa que cualquier modificación del contenido de la estructura que se realice dentro de la función a la que se pasa, no afectará a la estructura utilizada como argumento. Cuando se utilice una estructura como parámetro, el aspecto más importante a tener en cuenta es que el tipo del argumento debe coincidir con el tipo del parámetro. La mejor forma de conseguir esto consiste en definir una estructura global y, a continuación, utilizar su nombre de etiqueta para declarar variables de estructura y parámetros cuando se necesiten. #include <stdio.h> #include #include <math.h> #define PI 3.14159265358979 struct circulo { double xi, yi, xf, yf; }; void calculos_circulo(struct circulo); void main(void) { struct circulo c1; clrscr(); printf("Coordenadas del circulo: \n"); printf(" Centro x: "); scanf("%lf", &c1.xi); printf(" Centro y: "); scanf("%lf", &c1.yi); printf("Pto. exterior x: "); scanf("%lf", &c1.xf); printf("Pto. exterior y: "); scanf("%lf", &c1.yf); calculos_circulo (c1); getch(); } void calculos_circulo( struct circulo circ) __________________________________________________________________________________________________________ Estructuras Estáticas 29
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
{ double radio, area, proy_x, proy_y, circunferencia; proy_x = (circ.xf - circ.xi); proy_y = (circ.yf - circ.yi); radio = sqrtl( proy_x * proy_x + proy_y * proy_y ); //sqrtl calcula la raíz cuadrada de un double area = PI * radio * radio; circunferencia = 2 * PI * radio; printf("\nRadio: %.3lf\nArea : %.3lf\n", radio, area); printf("Circunferencia: %.3lf\n", circunferencia); }
•
Tablas y estructuras dentro de estructuras.
Un miembro de una estructura puede ser de un tipo simple (cualquier tipo predefinido) o compuesto (tablas, estructuras, etc.) . #include <stdio.h> #include #include <string.h> struct datosmotor { int cilindros; float potencia, ccubicos; }; struct equipamiento { char nombre_eq[21]; int precio_eq; }; struct autos_venta { char marca[31]; char modelo[31]; struct datosmotor motor; int precio; struct equipamiento opciones[10]; int puertas; }; void main(void) { struct autos_venta coche; clrscr(); strcpy(coche.marca, "SEAONOSEA"); coche.motor.cilindros = 8; coche.motor.potencia = 100.5; strcpy(coche.opciones[0].nombre_eq, "AIRE ACONDICIONADO"); coche.opciones[0].precio_eq = 300000; printf("Completa los datos\n\n"); printf("Marca %s Modelo: ", coche.marca); scanf("%s", coche.modelo); printf("Potencia %.1f Cent. cubicos: ", coche.motor.potencia); scanf("%d", &coche.motor.ccubicos); printf("Precio: "); scanf("%d", &coche.precio); printf("Tipo de extra: "); scanf("%s", coche.opciones[1].nombre_eq); printf("Precio extra: "); scanf("%d", &coche.opciones[1].precio_eq); getch(); }
__________________________________________________________________________________________________________ Estructuras Estáticas 30
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
APÉNDICE. Tratamiento de TABLAS y ESTRUCTURAS mediante PUNTEROS. En realidad, la notación de tablas es un método disfrazado de empleo de punteros. Dicho de otro modo, si se utiliza notación de tablas en el programa fuente, tendrá que ser traducida a notación de punteros a la hora de obtener el programa ejecutable. Por ejemplo: Sea un vector (no importa su tipo) como meses[12]. Se cumple que meses == &meses[0] Es decir, el nombre del vector representa un puntero (constante) dirigido al primer elemento del vector. Dicho de otra forma: el nombre del vector representa la dirección de memoria del primer elemento del vector (en realidad, de su primer byte). Los dos términos de la igualdad del ejemplo anterior son constantes punteros. Por tanto, no tiene sentido incrementarlos; o sea, no se puede usar la expresión: meses++ Algo semejante sería intentar incrementar una constante numérica, por ejemplo, el 5, de la forma 5++. Sin embargo, si es factible operar de la siguiente forma: meses + 1 al igual que lo sería emplear la expresión 5 + 1. Insistiendo en este punto y para comprender que el nombre del vector es un puntero -representación simbólica de una dirección constante- diremos que: del mismo modo que una variable no cambia su dirección (su lugar de almacenamiento) a lo largo de un programa aunque sí es posible que cambie su contenido, el nombre de un vector es una constante pues no puede cambiar su valor. Por otra parte, es posible asignar el nombre del vector a un puntero variable, el cual sí puede incrementarse o decrementarse. Por ejemplo: pt = meses; y luego... pt++; El ordenador direcciona cada byte individualmente, pero cuando sumamos 1 a un puntero (a una dirección), no se suma 1 byte, sino una unidad de almacenamiento del tipo al que pertenece el puntero. Por eso, al declarar un puntero hay que especificar al tipo de elementos al que va a referirse. Esto significa que, para vectores, al sumar 1 a un puntero (constante o variable) a ese vector, la dirección cambia al siguiente elemento del vector. Así pues, para utilizar los elementos del vector se puede emplear notación de tablas, pero es más efectiva la notación con punteros: __________________________________________________________________________________________________________ Estructuras Estáticas 31
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
ELEMENTO 1º 2º 3º ..... Nº
DIRECCIONES meses == &meses[0] meses + 1 == &meses[1] meses + 2 == &meses[2] ................. ................. meses+N - 1 == &meses[N-1]
VALORES *(meses) == meses[0] *(meses + 1) == meses[1] *(meses + 2) == meses[2] .................... ............. *(meses+N -1) == meses[N-1]
Por tanto, para referirnos a un elemento del vector id_array deberemos usar la expresión *(id_array + N - 1) siendo N el número de orden de ese elemento, considerando que el 1º elemento tiene nº de orden 1. Hay que tener en cuenta que el operador de indirección * tiene una prioridad muy alta, mayor que el operador suma +, por lo que hay que usar paréntesis cuando se quieren obtener los sucesivos elementos del vector. Por ejemplo: *(meses + 1) *meses + 1
hace referencia al valor del 2º elemento, y hace referencia al valor del 1º elemento más 1.
#include <stdio.h> #include short int meses[]={31,28,31,30,31,30,31,31,30,31,30,31}; #define TOPE sizeof (meses) / (sizeof (short int)) void main(void) { // Si comenzara en 1, se emplearía la int indice;// expresión: * ( meses + indice - 1 ) for(indice=0; indice < TOPE ; indice++) printf("Mes %2d = %d dias.\n", indice + 1, *(meses + indice) ); getch(); }
Ejemplo (repetido) de paso de vectores a funciones con parámetros puntero. #include <stdio.h> #include #define NUM_ALUMNOS 10 float fmedia (float * , int); // Observe la declaración de prototipo void main(void) // especialmente, el puntero: float * { float notas[NUM_ALUMNOS], nota_media; int i; for (i=0; i < NUM_ALUMNOS; i++ ) { printf("NOTA %d : ",i+1); scanf("%f", notas+i); // scanf necesita la dirección } nota_media = fmedia (notas, NUM_ALUMNOS); printf("Nota media = %5.2f ", nota_media ); getch(); } float fmedia ( float *vector, int num_elem ) __________________________________________________________________________________________________________ Estructuras Estáticas 32
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
{
float totnotas; int indice; for (indice=0, totnotas=0.0; indice < num_elem ; indice++) totnotas += *(vector+indice); return ( totnotas / num_elem ) ;
}
•
Punteros y vectores de caracteres. Siendo
char tira1[] = "Lenguaje C";
El nombre del vector tira1 es un puntero al primer elemento del mismo. Se cumplen las siguientes equivalencias: tira1==&tira1[0] *tira1=='L' *(tira1+1)==tira1[1]=='e' #include <string.h> #include <stdio.h> #include char m3[10]; void main(void) { char m1[30]={'L','e','n','g','u','a','j','e',' ','C','\0'}; static char m2[11]="Lenguaje C"; // longitud lógica= 10 caracteres printf("m3=%s Dir=%u \n\n", m3, m3 ); printf("m1=%s Dir=%u \n", m1, m1 ); // Lenguaje C 4239660 printf(" %c %c\n\n", *m1, *(m1+3) ); // L g printf("m2=%s Dir=%u\n", m2, m2 ); // Lenguaje C 4239690 printf(" %c %c\n\n", *m2, m2[5] ); // L a printf("\n\n Long. fisica Long. logica\n"); printf("m1=\t %d \t\t %d \n", sizeof m1, strlen(m1)); // 30 10 printf("m2=\t %d \t\t %d \n", sizeof m2, strlen(m2)); // 11 10 printf("m3=\t %d \t\t %d \n", sizeof m3, strlen(m3)); // 10 0 printf("\n\nAsignando nuevos valores:\n"); // Es ilegal m1 = "aaaa"; // Es ilegal m2 = "bbbbb"; // Es ilegal m3 = "ccccc"; // En su lugar hay que utilizar la función strcpy(); strcpy (m1, "aaaaa"); strcpy (m2, "bbbb"); strcpy (m3, "ccccc"); printf ("m1=%s \nm2=%s \nm3=%s \n ", m1, m2, m3); getch(); }
•
Punteros a char También se pueden usar punteros para definir una cadena: char *pttira = "Lenguaje C";
__________________________________________________________________________________________________________ Estructuras Estáticas 33
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
Tal declaración es equivalente a la utilizada anteriormente: char arraytira[] = "Lenguaje C"; pues ambas preparan un puntero a la cadena y reservan 11 elementos de almacenamiento estático. Sin embargo, existen notables diferencias... -
pttira es una variable, luego se puede incrementar (pttira++) o decrementar (pttira--). No hace falta que pttira sea estática, aunque puede serlo. Además de las posiciones de memoria reservadas para la cadena, existe otra reservada para el puntero: &pttira ............ printf("%c",pttira); // L pttira++; printf("%c",pttira); // e *pttira = 'a'; printf("%s",pttira); // anguaje C ..........................
-
Por su lado, arraytira es constante y .... arraytira == &arraytira[0] Luego no se puede incrementar (arraytira++ no es válido) aunque sí podemos operar *(arraytira + x) para referenciar al elemento x+1 de la cadena. .................... *(arraytira + 9) = 'B'; printf("%s",arraytira); ......................
// Lenguaje B
#include <stdio.h> #include int long_cad (char * ); void main (void) { int long1, long2; char nombre[40]; char *comida="una hilera de hormigas..."; // 25 caracteres printf("¿Cómo se llama el oso hormiguero? "); gets(nombre); // nombre y comida actúan como punteros cadenas long1 = long_cad (nombre); long2 = long_cad (comida); printf("%s, que mide %d, se comió ", nombre, long1);
a
__________________________________________________________________________________________________________ Estructuras Estáticas 34
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
puts(comida); printf("en total %d.", long2); getch();
} //***************************************************************** int long_cad (char *cadena) //esta función equivale a strlen() { char *ptrcad; ptrcad = cadena; while (*ptrcad != '\0') ptrcad++; return (ptrcad - cadena); }
•
Punteros y tablas multidimensionales.
Una tabla de dos dimensiones se puede considerar como una tabla unidimensional de tablas unidimensionales, osea, un vector de vectores. Supongamos la definición: int bid[3][5] = { {4, 9, 0}, {0}, {3, 1}
};
resulta que bid es el nombre de una tabla bidimensional, o sea, bid es un vector de vectores. Dicho de otra forma: bid contiene 3 elementos, cada uno de los cuales es un vector de 5 enteros. 4 9 0 bid[0]
0
0
0 0 0 bid[1]
0
0
3 1 0 bid[2]
0
0
Los punteros que apuntan a cada uno de estos 3 vectores son bid[0], bid[1] y bid[2]. El nombre de la tabla bid apunta al primer elemento de la tabla que es un vector de 5 enteros, o sea, a bid[0]. A su vez, bid[0] es un puntero a su primer elemento: bid[0][0]. Resumiendo, bid apunta a bid[0] que es un vector de int, y bid[0] apunta a bid[0][0] que es un entero. Por tanto, bid es un PUNTERO A UN PUNTERO . Sin embargo, coinciden... bid
==
bid[0] == &bid[0][0]
Del mismo modo: bid[1] == &bid[1][0] bid[2] == &bid[2][0] ..................................... No obstante, hay una diferencia: bid[0] apunta a un entero, o sea, a un objeto de 4 bytes de largo. __________________________________________________________________________________________________________ Estructuras Estáticas 35
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
bid apunta a un vector de 5 enteros, o sea, a un objeto de 20 bytes de largo. Por ello, al sumar 1 a bid[0] obtenemos una dirección 4 bytes superior (la del elemento siguiente), pero al sumar 1 a bid obtenemos una dirección 20 bytes superior (la del vector siguiente). #include <stdio.h> #include #define FILA 3 #define COLU 5 void main(void) { int tabla[FILA][COLU]= {{21,23,25,27,29},{42,43,44,45,46},{1,2,3,4,5}};
int f,c ; for (f=0; f< FILA; f++) //Duplica el valor de cada elmto tabla[f][c] for (c=0; c < COLU ; c++ ) * ( *(tabla+f) + c ) *= 2 ; for (f=0; f < FILA; f++) { for (c=0; c < COLU ; c++ ) printf("%5d", * ( *(tabla+f)+c)); //printf("%5d", tabla[f][c]); printf("\n"); } getch(); }
Al igual que bid, la tabla bidimensional tabla del ejemplo anterior se puede considerar un vector de vectores. Así pues, tabla + 0 apunta a tabla[0] que es un vector de 5 enteros y que a su vez apunta a tabla[0][0];tabla + 1 apunta a tabla[1] que es otro vector de 5 enteros y que a su vez apunta a tabla[1][0], etc. Al aplicar el operador de indirección: * (tabla + f ) obtendremos la dirección de tabla[0], de tabla[1], etc. según «f » vaya tomando los valores 0, 1, etc. Si a la expresión anterior le sumamos el valor «c » tendremos la dirección del elemento de índice « f ,c ». Es decir, *(tabla + 0 ) + 0 nos da la dirección de tabla[0][0] *(tabla + 0 ) + 1 nos da la dirección de tabla[0][1] ................... *(tabla + 1 ) + 0 nos da la dirección de tabla[1][0] ................... Por último, para acceder al valor que guardan tales elementos, basta aplicar el operador de indirección. *( * ( tabla + f
) + c )
__________________________________________________________________________________________________________ Estructuras Estáticas 36
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
•
Punteros a estructuras.
Los punteros a estructuras se declaran colocando el símbolo * delante del nombre de la variable de estructura. Para conocer la dirección de una variable de estructura se debe colocar el operador & delante del nombre de la estructura. Para acceder a los miembros de la estructura a través de un puntero a esa estructura, se debe utilizar el operador flecha (símbolos menos y mayor que: ->) . #include <stdio.h> #include #include #define DELAY 960000 struct mi_tiempo { int horas, minutos, segundos; }; void actualiza (struct mi_tiempo *t), muestra (struct mi_tiempo *t); void retraso(void); void resetea (struct mi_tiempo *t); int main(void) { struct mi_tiempo crono; unsigned char tecla; crono.horas = 23; crono.minutos = 58; crono.segundos = 55; while(1) {actualiza(&crono); gotoxy(1,1); muestra(&crono); if(kbhit()) { tecla = toupper(getch()); if (tecla == 'R') resetea(&crono); else return 0; } } } void resetea (struct mi_tiempo *t) { t->horas = t->minutos = t->segundos = 0; } void actualiza (struct mi_tiempo *t) { t->segundos++; if(t->segundos==60) { t->segundos = 0; t->minutos++; } if(t->minutos==60) { t->minutos = 0; t->horas++; } if(t->horas==24) t->horas = 0; retraso(); __________________________________________________________________________________________________________ Estructuras Estáticas 37
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
} void muestra(struct mi_tiempo *t) { printf("%02d:%02d:%02d", t->horas, t->minutos, t->segundos); } void retraso(void) { long int t; for(t=1; t
#include <stdio.h> #include #include <math.h> #define PI 3.14159265358979 struct circulo { double xi, yi, radio; }; void pidedatos_circulo( struct circulo * ); void main(void) { struct circulo c1; double area, circunferencia; clrscr; pidedatos_circulo ( &c1); area = PI * c1.radio * c1.radio ; circunferencia = 2 * PI * c1.radio; printf("\n\nCirculo (%.3lf , %.3lf) Radio=%.3lf\n\n", c1.xi, c1.yi, c1.radio); printf("Area: %.3lf \nCircunferencia: %.3lf \n", area, circunferencia ); getch(); } //************************************************************************* void pidedatos_circulo ( struct circulo *c ) { printf("Centro x: "); scanf("%lf", &c->xi); printf("Centro y: "); scanf("%lf", &c->yi); printf(" Radio: "); scanf("%lf", &c->radio); }
__________________________________________________________________________________________________________ Estructuras Estáticas 38