Вы находитесь на странице: 1из 47

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

INTRODUCCIN
Una computadora es una mquina que manipula informacin. Un curso de cualquier carrera relacionada a las ciencias computacionales incluye el estudio de cmo se organiza la informacin en una computadora, cmo se manipula y cmo se emplea. Por tanto, es sumamente importante que un estudiante de ciencias de la computacin comprenda los conceptos de organizacin y manipulacin de la informacin para continuar sus estudios en este campo. El presente curso tiene como finalidad que los alumnos comprendan, mediante un enfoque completamente prctico, el uso, la utilidad y la importancia que las estructuras de datos tienen en el amplio mundo de la informtica, particularmente en lo que a la materia de programacin se refiere. El texto presenta los conceptos abstractos, muestra la utilidad de tales conceptos en la resolucin de problemas y despus ensea cmo concretar las abstracciones usando un lenguaje de programacin. Se ha puesto especial nfasis en las versiones abstracta y concreta de un concepto, para que los estudiantes aprendan acerca del concepto mismo, su implementacin y aplicacin. El lenguaje empleado durante el curso es C (en sus diferentes versiones y/o derivaciones, tales como C++ y Visual C++). C es muy conveniente para el curso porque contiene las estructuras de control necesarias para hacer legibles los programas y permite poner en prctica de diversas maneras las estructuras de datos fundamentales, tales como los arreglos, las pilas, las colas, las listas enlazadas y los rboles. Esto hace que el estudiante aprecie las opciones, ventajas y desventajas que enfrenta un programador en una situacin real. C tambin se ha difundido con muchas computadoras diferentes y su popularidad sigue creciendo. Es un lenguaje agradable, expresivo y verstil. Aunque se requiere de un conocimiento previo de programacin o de los fundamentos de la misma, en este curso se pretende que los alumnos sin ningn tipo de experiencia en esta materia puedan asimilar de la mejor manera posible los conceptos tratados en el presente curso.

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

UNIDAD I. SUBRUTINAS (PROCEDIMIENTOS Y FUNCIONES). ARREGLOS (ARRAYS)


1.1. Subrutinas

En computacin, una subrutina o subprograma, como idea general, se presenta como un subalgoritmo que forma parte del algoritmo principal, el cual permite resolver una tarea especfica. Dicho de otro modo, es una porcin de cdigo que forma parte de un programa ms grande, del que relativamente es independiente. El concepto de subrutina as como su forma ha ido evolucionando, dando origen a los procedimientos y funciones. Una de sus derivaciones la representan los mtodos, nombre dado a los subprogramas que permiten el manejo de los objetos bajo el paradigma de la programacin orientada a objetos. Entre las ventajas de las subrutinas pueden citarse las siguientes: Permiten la reutilizacin de cdigo fuente. Permiten modularizar el cdigo fuente, simplificando la depuracin del mismo. Facilitan la comprensin e interpretacin del cdigo fuente. Permiten gestionar y utilizar ms eficientemente los recursos del sistema. Cuando se tiene una biblioteca de subrutinas previamente programada, el tiempo de desarrollo de una aplicacin disminuye considerablemente. Desde el punto de vista esttico, le dan una apariencia ms profesional a los programas.

1.1.1. Estructura de una subrutina Una subrutina es, sencillamente, un conjunto de sentencias que se pueden llamar desde cualquier parte de un programa. Las subrutinas permiten al programador un grado de abstraccin en la resolucin de un problema. Las subrutinas en C no se pueden anidar. Esto significa que una subrutina no se puede declarar dentro de otra (no se debe confundir declarar con invocar, llamar o ejecutar). La razn para esto es permitir un acceso muy eficiente a los datos. En C todas las subrutinas son externas o globales, es decir, pueden ser llamadas desde cualquier punto del programa. La estructura general de una subrutina en C es la siguiente:
<tipo de retorno> <nombre>( [<tipo de dato> <parmetro_1>, ..., <tipo de dato> <parmetro_N>] ) { <cuerpo de la subrutina>; [return( <expresin> );] }

donde: <tipo de retorno> Tipo de valor devuelto por la subrutina (funcin) o la palabra reservada void si la subrutina no devuelve ningn valor (procedimiento).

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

<nombre> <tipo de dato> <parmetro_1>, ..., <parmetro_N>

<cuerpo de la subrutina> <expresin>

Identificador o nombre de la subrutina. Tipo de dato del valor que recibe cada parmetro o argumento. Lista de declaraciones de los parmetros, separados por comas a los que se asignan los valores que recibe la subrutina para ser procesados. Sentencias o instrucciones que se ejecutan dentro de la subrutina. Valor que devuelve la subrutina (funcin).

Los aspectos ms sobresalientes en el diseo de una subrutina son: Tipo de resultado. Es el tipo de dato que devuelve la subrutina C (cuando se trata de una funcin; posteriormente se explicar la diferencia entre un procedimiento y una funcin) y aparece antes del nombre de la subrutina. Lista de parmetros. Es una lista de parmetros tipificados (con tipos). Cuerpo de la subrutina. Se encierra entre llaves de apertura ({) y cierre (}). No hay punto y coma despus de la llave de cierre. Paso de parmetros. Posteriormente se ver que el paso de parmetros en C se hace por valor o por referencia. No se pueden declarar funciones anidadas. Declaracin local. Las constantes, tipos de datos y variables declaradas dentro de la subrutina son locales a la misma y no perduran fuera de ella. Valor devuelto por la subrutina. Mediante la palabra reservada return se devuelve el valor de la subrutina (funcin).

Una llamada a la subrutina produce la ejecucin de las sentencias o instrucciones del cuerpo de la misma y un retorno a la unidad de programa llamadora despus que la ejecucin de la subrutina se ha terminado, normalmente cuando se encuentra una sentencia return.

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

Ejemplo 1.1 Se necesita construir un programa que permita determinar cul de tres nmeros enteros positivos es mayor en cada una de dos series diferentes.
#include <stdio.h> #include <conio.h> void main() { int nNum_A, nNum_B, nNum_C, nNum_X, nNum_Y, nNum_Z, nMayor; clrscr(); printf( "PRIMERA SERIE DE TRES NUMEROS\n" ); printf( "-----------------------------\n" ); printf( "Proporcione el valor para el primer numero: " ); scanf( "%d", &nNum_A ); printf( "Proporcione el valor para el segundo numero: " ); scanf( "%d", &nNum_B ); printf( "Proporcione el valor para el tercer numero: " ); scanf( "%d", &nNum_C ); nMayor = nNum_A; if( nMayor < nNum_B ) nMayor = nNum_B; if( nMayor < nNum_C ) nMayor = nNum_C; printf( "\nEl numero mayor es %d\n\n", nMayor ); printf( "SEGUNDA SERIE DE TRES NUMEROS\n" ); printf( "-----------------------------\n" ); printf( "Proporcione el valor para el primer numero: " ); scanf( "%d", &nNum_X ); printf( "Proporcione el valor para el segundo numero: " ); scanf( "%d", &nNum_Y ); printf( "Proporcione el valor para el tercer numero: " ); scanf( "%d", &nNum_Z ); nMayor = nNum_X; if( nMayor < nNum_Y ) nMayor = nNum_Y; if( nMayor < nNum_Z ) nMayor = nNum_Z; printf( "\nEl numero mayor es %d", nMayor ); getch(); }

En esta versin del programa puede notarse que en ambas series de nmeros, el bloque de cdigo para ejecutar el proceso para determinar cul de los tres nmeros es mayor se reescribe en cada serie.

El cdigo anterior resuelve el problema planteado en el ejemplo 1.1, pero como es posible apreciar, el bloque de cdigo que ejecuta el proceso para determinar cul de tres nmeros es mayor se repite en ambas series. Ahora bien, si en lugar de ejecutar el proceso para una o dos series de nmeros se tuviera que ejecutar para 5 o ms veces, esta labor aparte de ser tediosa, ocasionara un consumo mayor de los recursos del sistema. El problema anterior es posible resolverlo mediante el uso de una subrutina (en este caso, una funcin que devuelva o retorne el resultado) que permita reutilizar su cdigo en diferentes partes del cuerpo del programa principal o en otros programas que requieran su uso. El cdigo siguiente muestra la solucin del ejemplo 1.1 mediante el uso de una subrutina, en este caso una funcin:

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

#include <stdio.h> #include <conio.h> int numeroMayor( int nValor1, int nValor2, int nValor3 ); void main() { int nNum_A, nNum_B, nNum_C, nNum_X, nNum_Y, nNum_Z, nMayor; clrscr(); printf( "PRIMERA SERIE DE TRES NUMEROS\n" ); printf( "-----------------------------\n" ); printf( "Proporcione el valor para el primer numero: " ); scanf( "%d", &nNum_A ); printf( "Proporcione el valor para el segundo numero: " ); scanf( "%d", &nNum_B ); printf( "Proporcione el valor para el tercer numero: " ); scanf( "%d", &nNum_C ); nMayor = numeroMayor( nNum_A, nNum_B, nNum_C ); printf( "\nEl numero mayor es %d\n\n", nMayor ); printf( "SEGUNDA SERIE DE TRES NUMEROS\n" ); printf( "-----------------------------\n" ); printf( "Proporcione el valor para el primer numero: " ); scanf( "%d", &nNum_X ); printf( "Proporcione el valor para el segundo numero: " ); scanf( "%d", &nNum_Y ); printf( "Proporcione el valor para el tercer numero: " ); scanf( "%d", &nNum_Z ); nMayor = numeroMayor( nNum_X, nNum_Y, nNum_Z ); printf( "\nEl numero mayor es %d", nMayor ); getch(); }

Se declara el prototipo de la funcin, indicando el tipo de dato que retornar.

La funcin es llamada desde el cuerpo del programa principal, almacenando el valor devuelto por sta en una variable de memoria.

Tipo de dato a retornar Nombre de la funcin

Lista de parmetros (incluyendo el tipo de dato para cada uno y separados por coma)

int numeroMayor( int nValor1, int nValor2, int nValor3 ) { int nMayor = nValor1; if( nMayor < nValor2 ) nMayor = nValor2; if( nMayor < nValor3 ) nMayor = nValor3; return( nMayor ); }

Cdigo de la funcin que ejecuta el proceso para determinar cul de tres nmeros es mayor.

Valor de retorno

1.1.2. Nombre de una subrutina Un nombre de una subrutina comienza con una letra o un subrayado (_) y puede contener tantas letras, nmeros o subrayados como desee. El compilador ignora, sin embargo, a partir de una cantidad dada (32 en Borland/Inprise C, 248 em Microsoft). C es sensible a maysculas, lo que significa que las letras maysculas y minsculas son distintas a efectos del nombre de la subrutina. int max( int x, int y ); double media( double x1, double x2 ); double MAX( int* m, int n ); /*nombre de la funcin max*/ /*nombre de la funcin media*/ /*nombre de la funcin MAX*/

1.1.3. Tipo de dato de retorno Si la subrutina no devuelve un valor int, se debe especificar el tipo de dato devuelto (de retorno) por la misma; cuando devuelve un valor int, se puede omitir ya que por defecto C asume que todas las subrutinas son enteras, a pesar de ello siempre es conveniente especificar el tipo an siendo de tipo int, para mejor legibilidad. El tipo debe

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

ser uno de los tipos simples de C, tales como int, char o float, o un puntero a cualquier tipo C, o un tipo struct. int max( int x, int y ) double media( double x1, double x2 ) float func0(){ ... } char func1(){ ... } int *func3(){ ... } char *func4() { ... } /*devuelve un tipo int*/ /*devuelve un tipo double*/ /*devuelve un tipo float*/ /*devuelve un tipo char*/ /*devuelve un puntero a int*/ /*devuelve un puntero a char*/

Si una subrutina no devuelve un resultado, se puede utilizar el tipo void, que se considera como un tipo de dato especial. Muchas subrutinas no devuelven resultados. La razn es que se utilizan para realizar una tarea concreta. Una subrutina que no devuelve un resultado se denomina procedimiento. Para indicar al compilador que una subrutina no devuelve resultado, se utiliza el tipo de retorno void, como en este ejemplo: void verResultados( float fTotal, int nElementos ); Si se omite un tipo de retorno para una subrutina, como en verResultados( float fTotal, int nElementos ); el compilador asume que el tipo de dato devuelto es int. Aunque el uso de int es opcional, por razones de claridad y consistencia se recomienda su uso. As, la subrutina anterior se puede declarar tambin: int verResultados( float fTotal, int nElementos ); Como ya se ha mencionado anteriormente, a una subrutina que no devuelve un resultado se le denomina procedimiento; entonces, la diferencia entre un procedimiento y una subrutina denominada funcin, es que sta ltima siempre devolver o retornar un valor de un tipo especfico. 1.1.4. Resultados de una funcin Una funcin puede devolver un nico valor. El resultado se muestra con una sentencia return cuya sintaxis es: return( <expresin> ); El valor devuelto (<expresin>) puede ser cualquier tipo de dato excepto una funcin o un arreglo (array). Se pueden devolver valores mltiples devolviendo un puntero o una estructura. El valor de retorno debe seguir las mismas reglas que se aplican a un operador de asignacin. Por ejemplo, no se puede devolver un valor int si el tipo de retorno es un puntero. Sin embargo, si se devuelve un int y el tipo de retorno es un float, se realiza la conversin automticamente (conversin de tipos implcita). Una funcin puede tener cualquier nmero de sentencias return. Tan pronto como el programa encuentra cualquiera de las sentencias return, devuelve el control a la sentencia llamadora. La ejecucin de la funcin termina si no se encuentra ninguna

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

sentencia return; en este caso, la ejecucin contina hasta la llave final del cuerpo de la funcin. Si el tipo de retorno es void, la sentencia return se debe omitir. void func1( void ) { puts( Esta funcin no devuelve valores ); } El valor devuelto se suele encerrar entre parntesis, pero su uso es opcional. En algunos sistemas operativos, como DOS, se puede devolver un resultado al entorno llamador. Normalmente el valor 0, se suele devolver en estos casos. int main() { puts( Prueba de un programa C, devuelve 0 al sistema ); return( 0 ); }

Un error tpico de programacin es olvidar incluir la sentencia return o situarla dentro de una seccin de cdigo que no se ejecute. Si ninguna sentencia return se ejecuta, entonces el resultado que devuelve la funcin es impredecible y puede originar que el programa falle o produzca resultados incorrectos. Por ejemplo, suponga que se sita la sentencia return dentro de una seccin de cdigo que se ejecuta condicionalmente, tal como: if( fTotal >= 0 ) return( fTotal ); Si fTotal es menor que cero, no se ejecuta la sentencia return y el resultado de la funcin es un valor aleatorio (C puede generar el mensaje de advertencia Function should return a value, que le ayudar a detectar este posible error). 1.1.5. Llamada a una subrutina Las subrutinas, para poder ser ejecutadas, han de ser llamadas o invocadas. Cualquier expresin puede contener una llamada a una subrutina que redirigir el control del programa a la subrutina nombrada. Normalmente la llamada a una subrutina se realizar desde la funcin principal main(), aunque naturalmente tambin podr ser llamada desde otra subrutina. La subrutina que llama a otra subrutina se denomina subrutina llamadora y la subrutina controlada se denomina subrutina llamada. La subrutina llamada que recibe el control del programa se ejecuta desde el principio y cuando termina (se alcanza la sentencia return, o la llave de cierre (}) si no se devuelve ningn valor) el control del programa vuelve y retorna a la funcin main() o a la subrutina llamadora si no es main.

ESTRUCTURAS DE DATOS
#include <stdio.h> #include <conio.h>

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

void main() { int x; ... x = modulo( 5, 3 ); ... muestraResultado( x, 10, 5 ); ... }

Se llaman o invocan las subrutinas dentro del cuerpo del programa principal y se transfiere el control hacia las subrutinas nombradas.

int modulo( int nDividendo, int nDivisor ) { int nResiduo; nResiduo = nDividendo - ( (int)( nDividendo / nDivisor ) * nDivisor ); return( nResiduo ); } void muestraResultado( int nValor, int nFila, int nColumna ) { clrscr(); gotoxy( nColumna, nFila ); printf( El residuo es %d, nValor ); }

Termina la ejecucin de las subrutinas y el control retorna o se devuelve al cuerpo del programa principal.

1.1.6. Prototipos de las subrutinas La declaracin de una subrutina se denomina prototipo. Los prototipos de una subrutina contienen la cabecera de la misma, con la diferencia de que los prototipos terminan con un punto y coma. Especficamente un prototipo consta de los siguientes elementos: nombre de la subrutina, una lista de argumentos o parmetros encerrados entre parntesis y un punto y coma. En C no es estrictamente necesario incluir el prototipo aunque s es recomendable para que el compilador pueda hacer chequeos en las llamadas a las subrutinas. Los prototipos de las subrutinas llamadas en un programa se incluyen en la cabecera del programa para que as sean reconocidas en todo el programa. La sintaxis para el prototipo de una subrutina es la siguiente: <tipo de retorno> <nombre>( [<lista de parmetros>] ); donde: <tipo de retorno> Tipo del valor devuelto por la subrutina (funcin) o palabra reservada void si no devuelve un valor (procedimiento). Nombre de la subrutina. Lista de declaracin de parmetros de la subrutina, separados por comas (los nombres de los parmetros son opcionales, pero es buena prctica incluirlos para indicar lo que representan).

<nombre> <lista de parmetros>

Un prototipo declara una subrutina y proporciona una informacin suficiente al compilador para verificar que la funcin est siendo llamada correctamente, con respecto al nmero y tipo de los parmetros y el tipo devuelto por la misma. Es obligatorio poner un

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

punto y coma al final del prototipo de la subrutina con el objeto de convertirlo en una sentencia. double gradosCelsius( double dFahrenheit ); /* prototipos vlidos */ int max( int x, int y ); int longitud( int h, int a ); struct persona entrada( void ); char* concatenar( char* cTexto1, char* cTexto2 ); double intensidad( double, double ); Los prototipos se sitan al principio de un programa, antes de la definicin de la funcin main(). El compilador utiliza los prototipos para validar que el nmero y los tipos de datos de los argumentos reales de la llamada de la subrutina son los mismos que el nmero y tipo de argumentos formales en la subrutina de llamada. Si se detecta una inconsistencia, se visualiza un mensaje de error. Sin prototipos, un error puede ocurrir si un argumento con un tipo de dato incorrecto se pasa a una subrutina. En programas complejos, este tipo de errores son muy difciles de detectar. En C, la diferencia entre los conceptos declaracin y definicin es preciso tenerla clara. Cuando una entidad se declara, se proporciona un nombre y se listan sus caractersticas. Una definicin proporciona un nombre de entidad y reserva espacio en memoria para esa entidad. Una definicin indica que existe un lugar en un programa donde existe realmente la entidad definida, mientras que una declaracin es slo una indicacin de que algo existe en una posicin. Una declaracin de la subrutina contiene slo la cabecera de la misma y una vez declarada, la definicin completa de la subrutina debe existir en algn lugar del programa, antes o despus de main(). Ejemplo 1.2 Se requiere un programa tal que, dado como datos la base y la altura, permita obtener el rea de un rectngulo.
#include <stdio.h> #include <conio.h> float areaRectangulo( float fBase, float fAltura ); /* PROTOTIPO O DECLARACIN */

void main() { float x, y; clrscr(); printf( "Proporcione el valor de la base : " ); scanf( "%f", &x ); printf( "Proporcione el valor de la altura: " ); scanf( "%f", &y ); printf( "\nEl area del rectangulo es %.2f", areaRectangulo( x, y ) ); getch(); } float areaRectangulo( float fBase, float fAltura ) { return( fBase * fAltura ); } /* DEFINICIN */

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

1.1.6.1.

Prototipos con un nmero no especificado de parmetros

Un formato especial de prototipo es aquel que tiene un nmero no especificado de argumentos, que se representa por puntos suspensivos (...). Por ejemplo: int muestras( int a, ... ); int printf( const char *formato, ... ); int scanf( const char *formato, ... ); Para implementar una subrutina con lista variable de parmetros es necesario utilizar unas macros (especie de funciones en lnea) que estn definidas en el archivo de cabecera stdarg.h, por consiguiente lo primero que se debe hacer es incluir dicho archivo. #include <stdarg.h> En el archivo est declarado el tipo va_list, un puntero para manejar la lista de datos pasada a la subrutina. va_list puntero; La funcin va_start() inicializa puntero, de tal forma que referencia al primer parmetro variable. El prototipo que tiene: void va_start( va_list puntero, ultimofijo ); El segundo argumento es el ltimo argumento fijo de la subrutina que se est implementando. As para la funcin muestras( int a, ... ); va_start( puntero, a ); Con la funcin va_arg() se obtienen, consecutivamente, los sucesivos argumentos de la lista variable, El prototipo que tiene tipo va_arg( va_list puntero, tipo ); Donde tipo es el tipo del argumento variable que es captado en ese momento, a su vez es el tipo de dato que devuelve va_arg(). Para la funcin muestras() si los argumentos variables son de tipo int: int m; m = va_arg( puntero, int ); La ltima llamada que se debe hacer en la implementacin de estas funciones es va_end(). De esta forma se queda el puntero preparado para siguientes llamadas. El prototipo que tiene va_end(): void va_end( va_list puntero );

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

Ejemplo 1.3 Una aplicacin completa de una subrutina con lista de argumentos variables es maximo( int, ... ), que calcula el mximo de n argumentos de tipo double, donde n es el argumento fijo que se utiliza.
#include <stdio.h> #include <conio.h> #include <stdarg.h>

/* REFERENCIA AL ARCHIVO DE CABACERA O LIBRERA */ /* PROTOTIPO */

void maximo( int n, ... );

void main() { clrscr(); puts( "\t\tBUSQUEDA DEL NUMERO MAXIMO\n" ); maximo( 6, 3, 4, -12.5, 1.2, 4.5, 6.4 ); getch(); } void maximo( int n, ... ) { double mx, actual; /* PUNTERO PARA MANEJAR LA LISTA DE DATOS */ va_list puntero; va_start( puntero, n ); /* REFERENCIA AL PRIMER PARMETRO VARIABLE */ mx = actual = va_arg( puntero, double ); /* SE OBTIENEN LOS PARMETROS DE LA LISTA VARIABLE */ printf( "\t\tArgumento actual: %.2lf\n", actual ); for( int i = 2; i <= n; i++ ) { actual = va_arg( puntero, double ); printf( "\t\tArgumento actual: %.2lf\n", actual ); if( actual > mx ) mx = actual; } printf( "\t\tMaximo de la lista de %d numeros es %.2lf\n", n, mx ); va_end( puntero ); /* SE LIBERA LA LISTA DE PARMETROS DE LA MEMORIA */ }

1.1.7. Parmetros de una subrutina C siempre utiliza el mtodo de parmetros por valor para pasar variables a subrutinas. Para que una subrutina devuelva un valor a travs de un argumento hay que pasar la direccin de la variable, y que el argumento correspondiente de la subrutina sea un puntero, es la forma de conseguir en C un paso de parmetro por referencia. Suponiendo que se tenga la declaracin de una subrutina circulo con tres argumentos void circulo( int x, int y, int diametro ); Cuando se llama a circulo se deben pasar tres parmetros a esta subrutina. En el punto de llamada cada parmetro puede ser una constante, una variable o una expresin, como en el siguiente ejemplo: circulo( 25, 40, vueltas * 4 ); 1.1.7.1. Paso de parmetros por valor

Paso por valor (tambin llamado paso por copia) significa que cuando C compila la subrutina y el cdigo que la llama, sta recibe una copia de los valores de los parmetros.

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

Si se cambia el valor de un parmetro variable local, el cambio slo afecta a la subrutina y no tiene efecto fuera de ella. En el ejemplo siguiente se describe la forma en que se efecta el paso de un parmetro por valor a una subrutina:

void main() { int i = 6; func( i ); }

void func( int i ) { printf( "%d", i ); i++; }

El mtodo por defecto de pasar parmetros es por valor, a menos que se pasen arreglos (arrays). Los arreglos se pasan siempre por direccin. Ejemplo 1.4 El siguiente programa muestra el mecanismo de paso de parmetros por valor.

/* Muestra el paso de par metros por valor. Se puede cambiar la variable del par metro en la funcin pero su modificacin no puede salir al exterior. */ #include <stdio.h> #include <conio.h> void DemoLocal( int nValor ); void main() { int n = 10; clrscr(); printf( "Antes de llamar a DemoLocal, n = %d\n", n ); DemoLocal( n ); printf( "Despues de llamada a DemoLocal, n = %d\n", n ); getch(); } void DemoLocal( int nValor ) { printf( "Dentro de DemoLocal, nValor = %d\n", nValor ); nValor = 999; printf( "Dentro de DemoLocal, nValor = %d\n", nValor ); }

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

1.1.7.2.

Paso de parmetros por referencia

Cuando una subrutina debe modificar el valor del parmetro pasado y devolver este valor modificado al programa que llama a la subrutina, se ha de utilizar el paso de parmetro por referencia o direccin. En este mtodo el compilador pasa la direccin de memoria del valor del parmetro a la subrutina. Cuando se modifica el valor del parmetro (la variable local), este valor queda almacenado en la misma direccin de memoria, por lo que al retornar al programa que llama a la subrutina la direccin de la memoria donde se almacen el parmetro contendr el valor modificado. Para pasar una variable por referencia, el smbolo & debe preceder al nombre de la variable y el parmetro variable correspondiente a la subrutina debe declararse como puntero (*). float x; int y; entrada( &x, &y ); ... void entrada( float* x, int* y ) C permite utilizar punteros para implementar parmetros por referencia, ya que por defecto en C el paso de parmetros es por valor. Ejemplo 1.5 En el programa siguiente se implementa una subrutina para intercambiar los valores de dos variables para ilustrar el mtodo de paso de parmetros por referencia.

#include <stdio.h> #include <conio.h> void intercambio( int* a, int* b ); void main() { int i = 3, j clrscr(); printf( "i = intercambio( printf( "i = getch(); }

= 50; %d y j = %d\n", i, j ); &i, &j ); %d y j = %d", i, j );

void intercambio( int* a, int* b ) { int nAux = *a; *a = *b; *b = nAux; }

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

1.1.8. Recursividad Una funcin recursiva es una funcin que se llama a s misma directa o indirectamente. La recursividad o recursin directa es el proceso por el que una funcin se llama a s misma desde el propio cuerpo de la funcin. La recursividad o recursin indirecta implica ms de una funcin. La recursividad indirecta implica, por ejemplo, la existencia de dos funciones: uno() y dos(). Suponga que main() llama a uno(), y a continuacin uno() llama a dos(). En alguna parte del proceso, dos() llama a uno() una segunda llamada a uno()-. Esta accin es recursividad indirecta, pero es recursiva, ya que uno() ha sido llamada dos veces, sin retornar nunca a su llamadora. Un proceso recursivo debe tener una condicin de terminacin, ya que si no puede continuar indefinidamente. Ejemplo 1.6 Construir un programa que obtenga el factorial de un nmero.
#include <stdio.h> #include <conio.h> double factorial( int nValor ); void main() { int n; clrscr(); printf( "Obtener el factorial de: " ); scanf( "%d", &n ); printf( "\nEl factorial de %d es %.0lf", n, factorial( n ) ); getch(); } double factorial( int nValor ) { if( nValor > 1 ) return( nValor * factorial( nValor - 1 ) ); return( 1 ); }

La obtencin del factorial de un nmero es un claro ejemplo para ilustrar la recursividad directa de una funcin.

Ejemplo 1.7 Determinar si un entero positivo es par o impar, con dos funciones que se llaman mutuamente.

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

#include <stdio.h> #include <conio.h> int par( int nValor ); int impar( int nValor ); void main() { int n; clrscr(); do{ printf( "\nEntero > 0: " ); scanf( "%d", &n ); }while( n <= 0 ); if( par( n ) ) printf( "El numero %d es par", n ); else printf( "El numero %d es impar", n ); getch(); } int par( int nValor ) { if( nValor == 0 ) return( 1 ); else return( impar( nValor - 1 ) ); } int impar( int nValor ) { if( nValor == 0 ) return( 0 ); else return( par( nValor - 1 ) ); }

El programa anterior permite ilustrar claramente la recursividad indirecta. La funcin par() llama a la funcin impar(), sta a su vez llama a la funcin par(). La condicin para terminar de hacer llamadas es que el parmetro nValor sea cero; el cero se considera par.

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

1.2.

Arreglos (Arrays)

Un arreglo (Array) es una secuencia de datos del mismo tipo. Los datos se llaman elementos del arreglo y se enumeran consecutivamente 0, 1, 2, 3, etc. El tipo de elementos almacenados en el arreglo puede ser cualquier tipo de dato de C, incluyendo estructuras definidas por el usuario, como se describir ms adelante. Normalmente el arreglo se utiliza para almacenar tipos tales como char, int o float. Un arreglo puede contener, por ejemplo, la edad de los alumnos de una clase, las temperaturas de cada da de un mes en una ciudad determinada, o el nmero de personas que residen en diferentes localidades. Cada item del arreglo se denomina elemento. Como ya se ha comentado, los elementos de un arreglo se enumeran consecutivamente (iniciando la secuencia en el elemento 0). Estos nmeros se denominan valores ndice o subndices del arreglo. El trmino subndice se utiliza ya que se especifica igual que en matemticas, como una secuencia tal como a0, a1, a2, ..., an. Estos nmeros localizan la posicin del elemento dentro del arreglo, proporcionando acceso directo al arreglo. Si el nombre del arreglo es a, entonces a[0] es el nombre del elemento que est en la posicin 0, a[1] es el nombre del elemento que est en la posicin 1, etc. En general, el elemento n-simo est en la posicin n-1. De modo que si el arreglo contiene n elementos, sus nombres son a[0], a[1], ..., a[n-1]. Grficamente se representa as el arreglo a con seis elementos:

25.1 0

34.2 1

5.25 2

7.45 3

6.09 4

7.54 5

El arreglo a contiene seis elementos: a[0] contiene el valor 25.1, a[1] contiene el valor 34.2, a[2] contiene el valor 5.25, a[3] contiene el valor 7.45, a[4] contiene el valor 6.09 y a[5] contiene el valor 7.54. El diagrama anterior representa realmente una regin de la memoria de la computadora, ya que un arreglo se almacena siempre con sus elementos en una secuencia de posiciones de memoria contigua.

1.2.1. Declaracin de un arreglo Al igual que con cualquier tipo de variable, se debe declarar un arreglo antes de utilizarlo. Un arreglo se declara de modo similar a otros tipos de datos, excepto que se debe indicar al compilador el tamao, longitud o dimensin del arreglo se debe seguir al nombre, el tamao encerrado entre corchetes. La sintaxis para declarar un arreglo de una dimensin determinada es: <tipo> nombreArreglo[numero_de_elementos]; Por ejemplo, para crear un arreglo de diez variables enteras, se escribe: int numeros[10];

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

Esta declaracin hace que el compilador reserve espacio suficiente en memoria para contener diez valores enteros. Se puede acceder a cada elemento del arreglo utilizando un ndice en el nombre del mismo. Por ejemplo: printf( %d \n, numeros[4] ); visualiza el valor del elemento 5 del arreglo. Los arreglos siempre comienzan en el elemento 0. As pues, el arreglo numeros contiene los siguientes elementos individuales: numeros[0] numeros[1] numeros[2] numeros[3] numeros[4] numeros[5] numeros[6] numeros[7] numeros[8] numeros[9] Si por ejemplo, se quiere crear un arreglo de nmeros reales y su tamao es una constante representada por un parmetro: #define N 20; float vector[N]; Para acceder al elemento 3 y leer un valor de entrada: scanf( %f, &vector[2] ); 1.2.2. Subndices de un arreglo El ndice de un arreglo se denomina, con frecuencia, subndice del arreglo. El trmino procede de las matemticas, en las que un subndice se utiliza para representar un elemento determinado. numeros0 numeros3 equivale a equivale a numeros[0] numeros[3]

El mtodo de numeracin del elemento n-simo con el ndice o subndice n-1 se denomina indexacin basada en cero. Su uso tiene el efecto de que el ndice de un elemento del arreglo es siempre el mismo que el nmero de pasos desde el elemento inicial a[0] a ese elemento. Por ejemplo, a[3] est a 3 pasos o posiciones del elemento a[0]. La ventaja de este mtodo se ver de modo ms evidente al tratar las relaciones entre arreglos y punteros. En los programas se pueden referenciar elementos del arreglo utilizando frmulas para los subndices. Mientras que el subndice puede evaluar a un entero, se puede utilizar una constante, una variable o una expresin para el subndice. As, algunas referencias individuales a elementos son:

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

edad[4] ventas[total+5] bonos[mes] salario[mes[i]*5] 1.2.3. Almacenamiento en memoria de los arreglos Los elementos de los arreglos se almacenan en bloques contiguos. As, por ejemplo, los arreglos int edades[5]; char codigos[5]; se representan grficamente en memoria de la manera siguiente:

edades

[0] [1] [2] [3] [4]

codigos

[0] [1] [2] [3] [4]

Los arreglos de caracteres funcionan de igual forma que los arreglos numricos, partiendo de la base que cada carcter ocupa formalmente un byte. As, por ejemplo, un arreglo llamado nombre se puede representar de la siguiente manera:

nombre char nombre[] = Rosario; R o s a r i o [0] [1] [2] [3] [4] [5] [6] [7]

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

A tener en cuenta, en las cadenas de caracteres el sistema siempre inserta un ltimo carcter (nulo) para indicar el fin de la cadena. 1.2.4. El tamao de los arreglos El operador sizeof devuelve el nmero de bytes necesarios para contener su argumento. Si se usa sizeof para solicitar el tamao de un arreglo, esta funcin devuelve el nmero de bytes reservados para el arreglo completo. Por ejemplo, supongamos que se declara un arreglo de enteros de 100 elementos denominado edades; si se desea conocer el tamao del arreglo, se puede utilizar la sentencia similar a: n = sizeof( edades ); donde n tomar el valor 200. Si se desea solicitar el tamao de un elemento individual del arreglo, tal como n = sizeof( edades[6] ); n almacenar el valor 2 (nmero de bytes que contiene un entero). 1.2.5. Verificacin del rango del ndice de un arreglo C, al contrario de otros lenguajes de programacin, no verifica el valor del ndice de la variable que representa al arreglo. As, por ejemplo, en Pascal si se define un arreglo a con ndices 0 a 5, entonces a[6] har que el programa se rompa en tiempo de ejecucin.

Ejemplo 1.8 Proteccin frene a errores en el intervalo (rango) de valores de una variable de ndice que representa un arreglo.

double suma( const double a[], const int n ) { double S = 0.0; if( n * sizeof( double ) > sizeof( a ) ) return( 0 ); for( int i = 0; i < n; i++ ) S += a[i]; return( S ); }

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

1.2.6. Inicializacin de un arreglo Se deben asignar valores a los elementos de un arreglo antes de utilizarlos, tal como se asignan valores a variables. Para asignar valores a cada elemento del arreglo de enteros precios, se puede escribir: precios[0] = 10; precios[1] = 20; precios[2] = 30; precios[3] = 40; La primera sentencia fija precios[0] al valor 10, precios[1] al valor 20, etc. Sin embargo, este mtodo no es prctico cuando el arreglo contiene muchos elementos. El mtodo utilizado, normalmente, es inicializar el arreglo completo en una sola sentencia. Cuando se inicializa un arreglo, el tamao se puede determinar automticamente por las constantes de inicializacin. Estas constantes se separan por comas y se cierran entre llaves, como en los ejemplos siguientes: int numeros[6] = { 10, 20, 30, 40, 50, 60 }; int n[] = { 3, 4, 5 }; /* Declara un arreglo de 3 elementos */ char c[] = { E, S, C, U, E, L,A }; /* Declara un arreglo de 7 elementos */ El arreglo numeros tiene 6 elementos, n tiene 3 elementos y el arreglo c tiene 7 elementos. En lenguaje C los arreglos de caracteres, las cadenas, se caracterizan por tener un carcter final que indica el fin de la cadena, es carcter nulo (\0). Lo habitual es inicializar un arreglo de caracteres (una variable cadena) con una constante cadena. char s[] = Puesta de sol; El mtodo de inicializar arreglos mediante valores constantes despus de su definicin es adecuado cuando el nmero de elementos del arreglo es pequeo. Por ejemplo, para inicializar un arreglo de 10 enteros a los valores 10 a 1, y a continuacin visualizar dichos valores en un orden inverso, se puede escibir: int cuenta[10] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; for( int i = 9; i >= 0; i-- ) printf( \n cuenta descendente de %d = %d, i, cuenta[i] ); Pueden asignarse valores a un arreglo utilizando un ciclo for o while/do while, y ste suele ser el sistema ms empleado normalmente. Por ejemplo, para inicializar todos los valores del arreglo numeros al valor 0, se puede utilizar la sentencia siguiente: for( i = 0; i <= 5; i++ ) numero[i] = 0; debido a que el valor del subndice i vara de 0 a 5, cada elemento del arreglo numeros se inicializa y establece a cero.

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

Ejemplo 1.9 El siguiente programa lee ocho enteros; a continuacin visualiza la lista y la suma total de los nmeros.

#include <stdio.h> #include <conio.h> #define NUM 8 void main() { int nums[NUM], total = 0, i; for( i = 0; i < NUM; i++ ) { clrscr(); printf( "Por favor, ingrese el numero: " ); scanf( "%d", &nums[i] ); } printf( "\nLista de numeros: " ); for( i = 0; i < NUM; i++ ) { printf( "%d ", nums[i] ); total += nums[i]; } printf( "\nLa suma de los numeros es %d", total ); getch(); }
1.2.7. Arreglos de caracteres y cadenas de texto Una cadena de texto es un conjunto de caracteres, tales como ABCDEFG. C soporta cadenas de texto utilizando un arreglo de caracteres que contenga una secuencia de caracteres: char cadena[] = ABCDEFG; Es importante comprender la diferencia entre un arreglo de caracteres y una cadena de caracteres. Las cadenas contienen un carcter nulo al final del arreglo de caracteres.

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

E S C U E L A

E S C U E L A \0

Arreglo de caracteres

Cadena

Las cadenas se sealan incluyendo un carcter al final de la cadena: el carcter nulo (\0), cuyo valor en el cdigo ASCII es 0. El mtodo ms fcil de inicializar un arreglo de caracteres es hacer la inicializacin de la declaracin: char cadena[7] = ABCDEFG; El compilador inserta automticamente un carcter nulo al final de a cadena, de modo que la secuencia real sera:

cadena

\0

La asignacin de valores a cadena se puede hacer del modo siguiente: cadena[0] = A; cadena[1] = B; cadena[2] = C; cadena[3] = D; cadena[4] = E; cadena[5] = F; cadena[6] = G; cadena[7] = \0; Sin embargo, no se puede asignar una cadena a un arreglo del siguiente modo: cadena = ABCDEFG; Para copiar una constante cadena o copiar una variable de cadena a otra se debe utilizar la funcin de la biblioteca estndar strcpy() (copiar cadena). strcpy() permite copiar una constante de cadena en una cadena. Para copiar la cadena Abracadabra en el arreglo nombre, se puede escribir strcpy( nombre, Abracadabra ); /* Copia Abracadabra en nombre */

strcpy() aade un carcter nulo al final de la cadena. A fin de que no se produzcan errores en la sentencia anterior, se debe asegurar que el arreglo de caracteres nombre tenga elementos suficientes para contener la cadena situada a su derecha.

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

Ejemplo 1.10 Rellenar los elementos de un arreglo con nmeros reales positivos procedentes del teclado.
#include <stdio.h> #include <conio.h> #define MAX 10 float muestra[MAX]; void main() { int i; printf( "\nIngrese una lista de %d elementos positivos.\n", MAX ); for( i = 0; i < MAX; muestra[i] > 0 ? ++i : i ) scanf( "%f", &muestra[i] ); }

En el ciclo principal, slo se incrementa i si muestra[i] es positivo: muestra[i] > 0 ? ++i : i. Con este incremento condicional se consigue que todos los valores almacenados sean positivos. Ejemplo 1.11 Visualizar el arreglo muestra despus de introducir datos en el mismo, separndolos con el tabulador.
#include <stdio.h> #include <conio.h> #define MAX 10 float muestra[MAX]; void main() { int i; printf( "\nIngrese una lista de %d elementos positivos.\n", MAX ); for( i = 0; i < MAX; muestra[i] > 0 ? ++i : i ) scanf( "%f", &muestra[i] ); printf( "\nDatos leidos del teclado: " ); for( i = 0; i < MAX; ++i ) printf( "%f\t", muestra[i] ); getch(); }

En el ciclo principal, slo se incrementa i si muestra[i] es positivo: muestra[i] > 0 ? ++i : i. Con este incremento condicional se consigue que todos los valores almacenados sean positivos.

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

1.2.8. Arreglos multidimiensionales Los arreglos vistos anteriormente se conocen como arreglos unidimensionales, vectores o listas y se caracterizan por tener un solo subndice. Los arreglos multidimensionales son aquellos que tienen ms de una dimensin y, en consecuencia, ms de un ndice. Los arreglos ms usuales son los de dos dimensiones, conocidos tambin como tablas o matrices. Sin embargo, es posible crear arreglos de tantas dimensiones como requieran sus aplicaciones, esto es, tres, cuatro o ms dimensiones. Un arreglo de dos dimensiones equivale a una tabla con mltiples filas y mltiples columnas.
0 0 1 2 3 4 1 2 3 n

m Estructura de un arreglo de dos dimensiones

Obsrvese que en el arreglo bidimensional de la figura anterior, si las filas se etiquetan de 0 a m y las columnas de 0 a n, el nmero de elementos que tendr el arreglo ser el resultado del producto ( m + 1 ) * ( n + 1 ). El sistema de localizar un elemento ser por las coordenadas representadas por su nmero de fila y su nmero de columna (a, b). La sintaxis para la declaracin de un arreglo de dos dimensiones es: <tipo de dato> <nombre arreglo>[<nmero de filas>][<nmero de columnas>]; Algunos ejemplos de declaracin de tablas son: char Pantalla[25][80]; int puestos[6][8]; int equipos[4][30]; int matriz[4][2]; Al contrario que otros lenguajes, C requiere que cada dimensin est encerrada entre corchetes. La sentencia int equipos[4, 30]; no es vlida. Un arreglo de dos dimensiones en realidad es un arreglo de arreglos. Es decir, es un arreglo unidimensional, y cada elemento no es un valor, sino que cada elemento es otro arreglo.

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

1.2.9. Inicializacin de arreglos multidimensionales Los arreglos multidimensionales se pueden inicializar, al igual que los de una dimensin, cuando se declaran. La inicializacin consta de una lista de constantes separadas por comas y encerradas entre llaves, como en los ejemplos siguientes: int tabla[2][3] = { { 51, 52, 53 }, { 54, 55, 56 } }; int tabla[2][3] = { { 51, 52, 53 }, { 54, 55, 56 } int tabla[2][3] = }; o bien, de la manera tradicional: int tabla[2][3]; tabla[0][0] = 51; tabla[0][1] = 52; tabla[0][2] = 53; tabla[1][0] = 54; tabla[1][1] = 55; tabla[1][2] = 56; 1.2.10. Acceso a los elementos de los arreglos bidimensionales Se puede acceder a los elementos de un arreglo bidimensional de igual forma que a los elementos de un arreglo unidimensional. La diferencia reside en que en los elementos bidimensionales deben especificarse los ndices de la fila y la columna. a) Insercin de elementos: <nombre arreglo>[ndice fila][ndice columna] = <valor elemento>; b) Extraccin de elementos: <variable> = <nombre arreglo>[ndice fila][ndice columna];

Algunos ejemplos de inserciones: tabla[2][3] = 4.5; resistencias[2][4] = 50; asientosLibres[5][12] = 5; y de extraccin de valores: ventas = tabla[1][1]; dia = semana[3][6];

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

1.2.11. Acceso a elementos mediante ciclos Se puede acceder a los elementos de arreglos bidimensionales mediante ciclos anidados. Su sintaxis es: int indFila, indColumna; for( indFila = 0; indFila < numFilas; indFila++ ) for( indColumna = 0; indColumna < numColumnas; indColumna++ ) Procesar arreglo[indFila][indColumna]; Ejemplo 1.12 Define una tabla de discos, rellena la tabla con datos de entrada y se muestran por pantalla.
#include <stdio.h> #include <conio.h> void main() { float discos[2][4]; int fila, columna; clrscr(); /* Se ingresan los valores para cada elemento del arreglo */ for( fila = 0; fila < 2; fila++ ) for( columna = 0; columna < 4; columna++ ) scanf( "%f", &discos[fila][columna] ); /* Se extraen los valores para su visualizacin */ for( fila = 0; fila < 2; fila++ ) for( columna = 0; columna < 4; columna++ ) printf( "\n $ %.2f", discos[fila][columna] ); getch(); }

1.2.12. Arreglos de ms de dos dimensiones C proporciona la posibilidad de almacenar varias dimensiones, aunque raramente los datos del mundo real requieren ms de dos o tres dimensiones. El medio ms fcil de dibujar un arreglo de tres dimensiones es imaginar un cubo. Un arreglo tridimensional se puede considerar como un conjunto de arreglos bidimensionales combinados juntos para formar, en profundidad, una tercera dimensin. El cubo se construye con filas (dimensin vertical), columnas (dimensin horizontal) y planos (dimensin de profundidad). Por consiguiente, un elemento dado se localiza especificando su plano, fila y columna. Una definicin de un arreglo tridimensional equipos es: int equipos[3][15][10];

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

Un ejemplo tpico de un arreglo de tres dimensiones es el modelo libro, en el que cada pgina del libro es un arreglo bidimensional construido por filas y columnas. As, por ejemplo, cada pgina tiene 45 filas que forman las filas del arreglo y 80 caracteres por lnea, que forman las columnas del arreglo. Por consiguiente, si el libro tiene 500 pginas, existirn 500 planos y el nmero de elementos ser 500 x 45 x 80 = 1,800,000.

Planos u hojas

Filas

Columnas Representacin de un arreglo de tres dimensiones

El arreglo libro tiene [PAGINAS] [LINEAS] [COLUMNAS], que definen el tamao del mismo. El tipo de datos del arreglo es char, ya que los elementos son caracteres. Cmo se puede acceder a la informacin del libro? El mtodo ms fcil es mediante ciclos anidados. Dado que el libro se compone de un conjunto de pginas, el ciclo ms externo ser el ciclo de pgina; y el ciclo ms interno ser el de columnas. Esto significa que el ciclo de filas ser el intermedio entre los de pginas y columnas. El cdigo siguiente permite procesar el arreglo: int pagina, linea, columna; for( pagina = 0; pagina < PAGINAS; pagina++ ) for( fila = 0; fila < FILAS; fila++ ) for( columna = 0; columna < COLUMNAS; columna++ ) <procesar libro[pagina][fila][columna]>

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

Ejercicio 1.13 Comprobar si una matriz de nmeros enteros es simtrica respecto a la diagonal principal. La matriz se genera internamente, con la funcin random() y argumento N(8) para que la matriz tenga valores de 0 a 7. El tamao de la matriz se pide como dato de entrada. La funcin determina si la matriz es simtrica. La funcin main() genera matrices hasta encontrar una que sea simtrica y la escribe en pantalla.
#include #include #include #include <stdio.h> <conio.h> <stdlib.h> <time.h>

#define N 8 void gen_mat( int a[][N], int n ); int simetrica( int a[][N], int n ); void escribe_mat( int a[][N], int n ); int main( void ) { int a[N][N]; //define matriz de tamao mximo N int n, i, j; int es_sim; randomize(); clrscr(); do{ printf( "\nTamao de cada dimensin de la matriz, mximo %d: ", N ); scanf( "%d", &n ); }while( n < 2 || n > N ); do{ gen_mat( a, n ); es_sim = simetrica( a, n ); if( es_sim ) { puts( "\nEncontrada matriz simtrica.\n" ); escribe_mat( a, n ); } }while( !es_sim ); getch(); return( 0 ); } void gen_mat( int a[][N], int n ) { int i, j; for( i = 0; i < n; i++ ) for( j = 0; j < n; j++ ) a[i][j] = random( N ); } int simetrica( int a[][N], int n ) { int i, j; int es_simetrica; for( es_simetrica = 1, i = 0; i < n - 1 && es_simetrica; i++ ) { for( j = i + 1; j < n && es_simetrica; j++ ) if( a[i][j] != a[j][i] ) es_simetrica = 0; } return( es_simetrica ); } void escribe_mat( int a[][N], int n ) { int i, j; puts( "\tMatriz analizada" ); puts( "\t----------------\n" ); for( i = 0; i < n; i++ ) { putchar( '\t' ); for( j = 0; j < n; j++ ) printf( "%d %c", a[i][j], ( j == n - 1 ? '\n ' : ' ' ) ); } }

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

1.2.13. Utilizacin de arreglos como parmetros En C todos los arreglos se pasan por referencia (direccin). Esto significa que cuando se llama a una funcin y se utiliza un arreglo como parmetro, se debe tener cuidado de no modificar los arreglos en una funcin llamada. C trata automticamente la llamada a la funcin como si hubiera situado el operador de direccin & delante del nombre del arreglo.
void main() { char palabra[4] = ABCD; cambiar( palabra ); puts( palabra ); }

palabra

void cambiar( char c[4] ) { puts( c ); strcpy( c, AMA ); }

Paso de un arreglo por direccin

Dadas las declaraciones #define MAX 100 double datos[MAX]; se puede declarar una funcin que acepte un arreglo de valores double como parmetro. La funcin SumaDatos() puede tener el prototipo: double SumaDatos( double datos[MAX] ); Incluso mejor si se dejan los corchetes en blanco y se aade un segundo parmetro que indica el indica el tamao del arreglo: double SumaDatos( double datos[], int n ); A la funcin SumaDatos se pueden entonces pasar argumentos de tipo arreglo junto con un entero n, que informa a la funcin sobre cuntos valores contiene el arreglo. Por ejemplo, esta sentencia visualiza la suma de valores de datos del arreglo: printf( \nSuma = %lf, SumaDatos( datos, MAX ) ); La funcin SumaDatos no es difcil de escribir. Un simple ciclo while suma los elementos del arreglo y una sentencia return devuelve el resultado de nuevo al llamador:

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

double SumaDatos( double datos[], int n ) { double suma = 0; while( n > 0 ) suma += datos[n--]; return( suma ); } El cdigo que se utiliza para pasar un arreglo a una funcin incluye el tipo de elemento del arreglo y su nombre. El siguiente ejemplo incluye dos funciones que procesan arreglos. En ambas listas de parmetros, el arreglo a[] se declara en la lista de parmetros tal como double a[] El nmero real de elementos se pasa mediante una variable entera independiente. Cuando se pasa un arreglo a una funcin, se pasa realmente slo la direccin de la celda de memoria donde comienza el arreglo. Este valor se representa por el nombre del arreglo a. La funcin puede cambiar entonces el contenido del arreglo accediendo directamente a las celdas de memoria en donde se almacenan los elementos del arreglo. As, aunque el nombre del arreglo se pasa por valor, sus elementos se pueden cambiar como si se hubieran pasado por referencia.

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

Ejercicio 1.14 Paso de arreglos a funciones. En el ejemplo se lee un arreglo y se escribe. El arreglo tiene un tamao mximo, L, aunque el nmero real de elementos es determinado en la funcin leerArray(). El segundo argumento es, por tanto, un puntero para as poder transmitir por referencia y obtener dicho dato de la funcin.
#include <stdio.h> #include <conio.h> #define L 100 void leerArray( double a[], int* ); void imprimirArray( const double [], int n ); void main() { double a[L]; int n; leerArray( a, &n ); printf( "El array a tiene %d elementos, estos son\n ", n ); imprimirArray( a, n ); getch(); } void leerArray( double a[], int* num ) { int n = 0; puts( "Ingrese datos. Para terminar pulse 0.\n" ); for( ; n < L; n++ ) { printf( "%d: ", n ); scanf( "%lf", &a[n] ); if( a[n] == 0 ) break; } *num = n; } void imprimirArray( const double a[], int n ) { for( int i = 0; i < n; i++ ) printf( "\t%d: %lf\n", i,a[i] ); }

1.2.14. Paso de cadenas como parmetros La tcnica de pasar arreglos como parmetros se utiliza para pasar cadenas de caracteres a funciones. Las cadenas terminadas en nulo utilizan el primer mtodo dado anteriormente para controlar el tamao de un arreglo. Las cadenas son arreglos de caracteres. Cuando una cadena se pasa a una funcin, tal como strlen(), la funcin conoce que se ha almacenado el final del arreglo cuando ve un valor 0 en un elemento del arreglo. Las cadenas utilizan siempre un valor 0 para indicar que es el ltimo elemento del arreglo de caracteres. Este 0 es el carcter nulo del cdigo de caracteres ASCII.

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

Considrese estas declaraciones de una constante y una funcin que acepta un parmetro cadena y un valor de su longitud. #define MAXLON 128 void FuncDemo( char s[], int long ); El parmetro s es un arreglo de caracteres de longitud no especificada. El parmetro long indica a la funcin cuntos bytes ocupa (que puede ser diferente del nmero de caracteres almacenados en s). Dadas las declaraciones siguientes: char nombre[MAXLON] = Manuel Martnez; FuncDemo( nombre, MAXLON ); la primera lnea declara e inicializa un arreglo de caracteres llamado nombre, capaz de almacenar hasta MAXLON 1 caracteres ms un byte de terminacin, carcter nulo. La segunda lnea, pasa la cadena a la funcin. 1.2.15. Ordenacin de listas (arreglos unidimensionales) La ordenacin de arreglos es otra de las tareas usuales en la mayora de los programas. La ordenacin o clasificacin es el procedimiento por el cual se disponen los elementos de un arreglo en un orden especificado, tal como orden alfabtico u orden numrico.

6 1 2 4 7 18 3 5 14

1 2 3 4 5 6 7 14 18

18 14 7 6 5 4 3 2 1

Lista desordenada

Lista ordenada (ascendente)

Lista ordenada (descendente)

Lista de nmeros desordenados y ordenada en orden ascendente y en orden descendente

Un diccionario es un ejemplo de una lista ordenada alfabticamente, y una lista de cuentas de un banco es un ejemplo de una lista ordenada numricamente. El orden de clasificacin u ordenacin puede ser ascendente o descendente. Existen diversos algoritmos de ordenacin de arreglos, tales como: burbuja, insercin, seleccin, rpido (quick sort), entre otros. 1.2.15.1. Mtodo de la burbuja

La ordenacin por burbuja es uno de los mtodos ms fciles de ordenacin. El mtodo de ordenacin es muy simple. Se compara cada elemento del arreglo con el siguiente (por parejas), si no estn en el orden correcto, se intercambian entre s sus

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

valores. El valor ms pequeo flota hasta la parte superior del arreglo como si fuera una burbuja en un vaso de refresco con gas. Se van realizando diferentes pasadas hasta que la lista se encuentra ordenada totalmente en orden ascendente.

Ejemplo 1.15 El programa siguiente ordena una lista de nmeros reales y a continuacin los imprime.

#include <stdio.h> #include <conio.h> void imprimir( float a[], int n ); void intercambio( float* x, float* y ); void ordenar( float a[], int n ); void main() { float a[10] = { 25.5, 34.1, 27.6, 15.24, 3.27, 5.14, 6.21, 7.57, 4.61, 5.4 }; clrscr(); imprimir( a, 10 ); ordenar( a, 10 ); imprimir( a, 10 ); getch(); } void imprimir( float a[], int n ) { for( int i = 0; i < n - 1; i++ ) printf( "%.2f, %c", a[i], ( ( i + 1 ) % 10 == 0 ? '\n' : ' ' ) ); printf( "%.2f \n", a[n-1] ); } void intercambio( float* x, float* y ) { float aux = *x; *x = *y; *y = aux; } void ordenar( float a[], int n ) { for( int i = n - 1; i > 0; i-- ) for( int j = 0; j < i; j++ ) if( a[j] > a[j+1] ) intercambio( &a[j], &a[j+1] ); }

1.2.15.2.

Mtodo de ordenacin por insercin

Una ordenacin por insercin comienza al considerar los dos primeros elementos del arreglo a, loas cuales son a[0] y a[1]. Si no estn ordenados, ocurre un intercambio. Entonces, se considera el tercer elemento a[2] y se inserta en su lugar adecuado. Si a[2] es menor que a[0] y a[1], estos dos elementos se recorren una posicin; a[0] se coloca en la posicin 1, a[1] en la posicin 2 y a[2] en la posicin 0. Si a[2] es menor que a[1] y no es menor que a[0], entonces a[1] se mueve a la posicin 2 y su lugar es ocupado por a[2]. Si por ltimo, a[2] no es menor que sus dos predecesores, permanece en su posicin

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

actual. Cada elemento a[i] se inserta en su localidad apropiada j de modo que 0 j i, y todos los elementos mayores que a[i] se mueven una posicin. En este mtodo, la ordenacin est restringida a solo una fraccin del arreglo en cada iteracin, y el arreglo se considera completo con la ltima pasada. Ejemplo 1.16 El programa siguiente ordena la misma lista empleada en el ejemplo 1.15, pero ahora utilizando el mtodo de insercin.
#include <stdio.h> #include <conio.h> void imprimir( float a[], int n ); void ordenar( float a[], int n ); void main() { float a[10] = { 25.5, 34.1, 27.6, 15.24, 3.27, 5.14, 6.21, 7.57, 4.61, 5.4 }; clrscr(); imprimir( a, 10 ); ordenar( a, 10 ); imprimir( a, 10 ); getch(); } void imprimir( float a[], int n ) { for( int i = 0; i < n - 1; i++ ) printf( "%.2f, %c", a[i], ( ( i + 1 ) % 10 == 0 ? '\n' : ' ' ) ); printf( "%.2f \n", a[n-1] ); } void ordenar( float a[], int n ) { float tmp; for( int i = 1; i < n; i++ ) { tmp = a[i]; for( int j = i; ( j > 0 ) && ( tmp < a[j-1] ); j-- ) a[j] = a[j-1]; a[j] = tmp; } }

1.2.15.3.

Mtodo de ordenacin por seleccin

Este mtodo de ordenacin se basa en el siguiente algoritmo. Se selecciona el menor elemento del arreglo global y se intercambia con el que ocupa la primera posicin. Ahora, tendremos un subarreglo que va desde la segunda posicin hasta el final. Se selecciona en este subarreglo el menor elemento y se intercambia con el que ocupa la segunda posicin del arreglo global. Quedar desordenado un subarreglo que va desde la tercera posicin hasta el final. Este proceso se repite hasta que solo nos quede ordenar un subarreglo de un solo elemento.

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

Ejemplo 1.17 Tomando como base el arreglo de los ejemplos anteriores, en el siguiente programa se implementa el mtodo de ordenacin por seleccin para el arreglo a.
#include <stdio.h> #include <conio.h> void imprimir( float a[], int n ); void ordenar( float a[], int n ); void main() { float a[10] = { 25.5, 34.1, 27.6, 15.24, 3.27, 5.14, 6.21, 7.57, 4.61, 5.4 }; clrscr(); imprimir( a, 10 ); ordenar( a, 10 ); imprimir( a, 10 ); getch(); } void imprimir( float a[], int n ) { for( int i = 0; i < n - 1; i++ ) printf( "%.2f, %c", a[i], ( ( i + 1 ) % 10 == 0 ? '\n' : ' ' ) ); printf( "%.2f \n", a[n-1] ); } void ordenar( float a[], int n ) { float tmp; int pos_min; for( int i = 0; i < n - 1; i++ ) { pos_min = i; for( int j = i + 1; j < n; j++ ) if( a[j] < a[pos_min] ) pos_min = j; tmp = a[i]; a[i] = a[pos_min]; a[pos_min] = tmp; } }

1.2.15.4.

Mtodo de ordenacin rpido (quick sort)

Este mtodo de ordenacin se basa en el siguiente algoritmo: 1) Se toma un elemento arbitrario del vector, al que se denomina pivote (p), 2) Se divide el vector de tal forma que todos los elementos a la izquierda del pivote sean menores que l, mientras que los que quedan a la derecha son mayores que l, 3) Se ordenan por separado las dos zonas delimitadas por el pivote.

Obtencin del pivote Mientras queden elementos mal colocados respecto al pivote: 1. Se recorre el vector de izquierda a derecha, hasta encontrar un elemento situado en una posicin i tal que a[i] > p.

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

2. Se recorre el vector de derecha a izquierda, hasta encontrar otro elemento situado en una posicin j tal que a[j] < p. 3. Se intercambian los elementos situados en las casillas i y j (de modo que, ahora a[i] < p < v[j]). Ejemplo 1.18 En el siguiente programa se implementa el mtodo de ordenacin rpida (quick sort) para el arreglo a utilizado en los programas de implementacin de los mtodos de ordenacin anteriores.
#include <stdio.h> #include <conio.h> void imprimir( float a[], int n ); void ordenar( float a[], int izqda, int dcha ); int partir( float a[], int primero, int ultimo ); void main() { float a[10] = { 25.5, 34.1, 27.6, 15.24, 3.27, 5.14, 6.21, 7.57, 4.61, 5.4 }; clrscr(); imprimir( a, 10 ); ordenar( a, 0, 9 ); imprimir( a, 10 ); getch(); } void imprimir( float a[], int n ) { for( int i = 0; i < n - 1; i++ ) printf( "%.2f, %c", a[i], ( ( i + 1 ) % 10 == 0 ? '\n' : ' ' ) ); printf( "%.2f \n", a[n-1] ); } void ordenar( float a[], int izqda, int dcha ) { int pivote; if( izqda < dcha ) { pivote = partir( a, izqda, dcha ); ordenar( a, izqda, pivote - 1 ); ordenar( a, pivote + 1, dcha ); } } int partir( float a[], int primero, int ultimo ) { float pivote = a[primero], temporal; int izqda = primero + 1; int dcha = ultimo; do //pivotear { while( ( izqda <= dcha ) && ( a[izqda] <= pivote ) ) izqda++; while( ( izqda <= dcha ) && ( a[dcha] > pivote ) ) dcha--; if( izqda < dcha ) { temporal = a[izqda]; a[izqda] = a[dcha]; a[dcha] = temporal; dcha--; izqda++; } }while( izqda <= dcha ); //Colocar el pivote en su sitio temporal = a[primero]; a[primero] = a[dcha]; a[dcha] = temporal; return( dcha ); //posicin del pivote }

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

UNIDAD II. ESTRUCTURAS, UNIONES Y ENUMERACIONES


La presente unidad examina estructuras, uniones, enumeraciones y tipos definidos por el usuario que permiten a un programador crear nuevos tipos de datos. La capacidad para crear nuevos tipos es una caracterstica importante y potente de C y libera a un programador de restringirse al uso de los tipos ofrecidos por el lenguaje. Adems de los conceptos anteriores, se abordar el, sin duda alguna, importante tema de los punteros, cuyo uso y aprendizaje tiene la fama, en el mundo de la programacin, de cierto grado de dificultad. Llegado el momento, se tratar de mostrar que los punteros no son ms difciles de aprender que cualquier otra herramienta de programacin ya examinada o por examinar a lo largo del presente curso. El puntero, no es ms que una herramienta muy potente que puede utilizar en sus programas para hacerlos ms eficientes y flexibles. El lenguaje C no tiene datos predefinidos tipo cadena (string). En su lugar C, manipula cadenas mediante arreglos de caracteres que terminan con el carcter nulo ASCII (\0). Por este motivo, el tema de las cadenas en C merece un apartado durante el estudio de la presente unidad.

2.1.

Estructuras

Los arreglos son estructuras de datos que contienen un nmero determinado de elementos (su tamao) y todos los elementos han de ser del mismo tipo de datos; es una estructura de datos homognea. Esta caracterstica supone una gran limitacin cuando se requieren grupos de elementos con tipos diferentes de datos cada uno. Por ejemplo, si se dispone de una lista de temperaturas, es muy til un arreglo; sin embargo, si se necesita una lista de informacin de clientes que contenga elementos tales como nombre, la edad, el domicilio, el nmero de la cuenta, etc., los arreglos no son adecuados. La solucin a este problema es utilizar un tipo de dato registro, en C llamado estructura. Los componentes individuales de una estructura se llaman miembros. Cada miembro (elemento) de una estructura puede contener datos de un tipo diferente de otros miembros. Por ejemplo, se puede utilizar una estructura para almacenar diferentes tipos de datos sobre una persona, tal como nombre, estado civil, edad y fecha de nacimiento. Cada uno de estos elementos se denomina nombre del miembro. En resumen, una estructura es una coleccin de uno o ms tipos de elementos denominados miembros, cada uno de los cuales puede ser de un tipo de dato diferente.

2.1.1. Declaracin de una estructura Una estructura es un tipo de dato definido por el usuario, que se debe declarar antes de que se pueda utilizar. El formato de la declaracin es:

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

struct <nombre de la estructura> { <tipo de dato miembro> <nombre miembro1>; <tipo de dato miembro> <nombre miembro2>; . . . <tipo de dato miembro> <nombre miembron>; }; Por ejemplo, supongamos que se desea almacenar los datos de una coleccin de discos compactos (CD) de msica. Estos datos pueden ser: Ttulo. Intrprete. Precio. Fecha de compra.

La estructura CD contiene cuatro miembros. Tras deducir los miembros, se debe decidir cules son los tipos de datos para utilizar por los miembros. La declaracin de la estructura CD quedara de la siguiente manera: struct CD { char titulo[30]; char interprete[25]; float precio; char fecha_compra[10]; }; 2.1.2. Definicin de variables de estructuras Al igual que los tipos de datos convencionales, a una estructura se accede utilizando una variable o variables que se deben definir despus de la declaracin de la estructura. Del mismo modo que sucede en otras situaciones, en C existen dos conceptos similares a considerar, declaracin y definicin. La diferencia tcnica es la siguiente, una declaracin especifica simplemente el nombre y formato de la estructura de datos, pero no reserva almacenamiento en memoria; la declaracin especifica un nuevo tipo de dato: struct <nombre_estructura>. Por consiguiente, cada definicin de variable para una estructura dada crea un rea en memoria en donde los datos se almacenan de acuerdo al formato estructurado declarado. Las variables de estructuras se pueden definir de dos formas: 1) listndolas inmediatamente despus de la llave de cierre de la declaracin de la estructura, o 2) listando el tipo de la estructura creado seguida por las variables correspondientes en cualquier lugar del programa antes de utilizarlas. As, la definicin y declaracin de la estructura CD se puede hacer por cualquiera de los dos mtodos:

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

#include <stdio.h> #include <conio.h> struct CD { char titulo[30]; char interprete[25]; float precio; char fecha_compra[10]; } cd1, cd2; . . . o bien: #include <stdio.h> #include <conio.h> struct CD { char titulo[30]; char interprete[25]; float precio; char fecha_compra[10]; }; void main() { CD cd1, cd2; . . . } 2.1.3. Uso de estructuras en asignaciones Como una estructura es un tipo de dato similar a un int o un char, se puede asignar una estructura a otra. Por ejemplo, se puede hacer que cd2 y cd3 tengan los mismos valores en sus miembros que cd1. Por consiguiente, sera necesario escribir las siguientes sentencias: cd2 = cd1; cd3 = cd1; De modo alternativo se puede escribir cd2 = cd3 = cd1;

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

2.1.4. Inicializacin de una declaracin de estructuras Se puede inicializar una estructura de dos formas. Se puede inicializar una estructura dentro de la seccin de cdigo de su programa, o bien se puede inicializar la estructura como parte de la definicin. Cuando se inicializa una estructura como parte de la definicin, se especifican los valores iniciales, entre llaves, despus de la definicin de variables de estructura. El formato general en este caso: struct <tipo> <nombre variable estructura> = { valor miembro1; valor miembro2; . . . valor miembron; }; struct CD { char titulo[30]; char interprete[25]; float precio; char fecha_compra[10]; } cd1 = { DARK SIDE OF THE MOON, PINK FLOYD, 235.50, 19/04/2009 }; o bien struct CD { char titulo[30]; char interprete[25]; float precio; char fecha_compra[10]; }; struct CD cd1 { DARK SIDE OF THE MOON, PINK FLOYD, 235.50, 19/04/2009 }; 2.1.5. El tamao de una estructura El operador sizeof se aplica sobre un tipo de datos, o bien sobre una variable. Se puede aplicar para determinar el tamao que ocupa en memoria una estructura. El siguiente programa ilustra el uso del operador sizeof para determinar el tamao de una estructura:

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

#include <stdio.h> #include <conio.h> struct CD { char titulo[30]; char interprete[25]; float precio; char fecha_compra[10]; }; void main() { CD cd1; printf( sizeof( CD ): %d\n, sizeof( cd1 ) ); getch(); } Al ejecutar el programa se produce la salida: sizeof( CD ): 69 El resultado se obtiene determinando el nmero de bytes que ocupa la estructura. CD titulo[30] interprete[25] precio fecha_compra[10] Total Miembros dato char (1) char (1) float (4) char (1) Tamao (bytes) 30 25 04 10 69

2.2.

Acceso a estructuras

Cuando se accede a una estructura, o bien se almacena informacin en la estructura o se recupera la informacin de la misma, se puede acceder a sus miembros de dos formas: 1) utilizando el operador punto (.), o bien 2) utilizando el operador puntero >. 2.2.1. Almacenamiento de informacin en estructuras Se puede almacenar informacin en una estructura mediante inicializacin, asignacin directa o lectura del teclado. El proceso de inicializacin ya se ha examinado, veamos ahora la asignacin directa y la lectura del teclado. Acceso a una estructura de datos mediante el operador punto La asignacin de datos a los miembros de una variable estructura se hace mediante el operador punto. La sintaxis C es: <nombre variable estructura>.<nombre miembro> = <valor>;

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

Algunos ejemplos son: strcpy( cd1.titulo, DARK SIDE OF THE MOON ); strcpy( cd1.interprete, PINK FLOYD ); cd1.precio = 235.50 strcpy( cd1.fecha_compra, 19/04/2009 ); El operador punto proporciona el camino directo al miembro correspondiente. Los datos que se almacenan en un miembro dado deben ser del mismo tipo que el tipo declarado para ese miembro. En el siguiente ejemplo se lee del teclado los datos de una variable estructura cd1: #include <stdio.h> #include <conio.h> struct CD { char titulo[30]; char interprete[25]; float precio; char fecha_compra[10]; }; void main() { CD cd1; clrscr(); printf( Ttulo : ); gets( cd1.titulo ); printf( Intrprete: ); gets( cd1.interprete ); printf( Precio : ); scanf( %f, &cd1.precio ); printf( Fecha de compra: ); gets( cd1.fecha_compra ); . . . }

Acceso a una estructura mediante el operador puntero El operador puntero, >, sirve para acceder a los datos de la estructura a partir de un puntero. Para utilizar este operador se debe definir primero una variable puntero para apuntar a la estructura. A continuacin, utilice simplemente el operador puntero para apuntar a un miembro dado. La asignacin de datos a estructuras utilizando el operador puntero tiene el formato: <puntero estructura>><nombre miembro> = <valor>;

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

As, por ejemplo, una estructura Estudiante struct Estudiante { int Matricula; char Nombre[41]; char Domicilio[81]; char Fecha_nac[10]; char Fecha_ing[10]; int cveCarrera; }; Se puede definir pEstudiante como puntero de estructura Estudiante *pEstudiante; Estudiante eRegistro; A los miembros de la estructura Estudiante se pueden asignar datos como sigue (siempre y cuando la estructura ya tenga creado su espacio de almacenamiento, por ejemplo, con malloc(); o bien, tenga la direccin de una variable estructura). pEstudiante = &eRegistro; strcpy( pEstudiante>Nombre, VICTOR AGUILERA ); strcpy( pEstudiante>Domicilio, 4. PONIENTE #1823 ); strcpy( pEstudiante>Fecha_nac, 12/06/1976 ); strcpy( pEstudiante>Fecha_ing, 11/08/2003 ); pEstudiante>cveCarrera = 3; 2.2.2. Lectura de informacin de una estructura Si ahora se desea introducir la informacin en la estructura basta con acceder a los miembros de la misma con el operador punto o el operador puntero. Se puede introducir la informacin desde el teclado o desde un archivo, o asignar valores calculados. As, si z es una variable de tipo estructura Complejo, se lee la parte real, parte imaginaria y se calcula el mdulo: #include <stdio.h> #include <conio.h> #include <math.h> struct Complejo { float pr; float pi; float modulo; };

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

void main() { Complejo z; printf( \nParte real: , ); scanf( %f, &z.pr ); printf( \nParte imaginaria: ); scanf( %f, &z.pi ); z.modulo = sqrt( pow( z.pr, 2 ) + pow( z.pi, 2 ) ); . . . } 2.2.3. Recuperacin de informacin de una estructura Se recupera informacin de una estructura utilizando el operador de asignacin o una sentencia de salida (printf(), puts(), etc.). Igual que antes, se puede emplear el operador punto o el operador puntero. El formato general toma uno de estos formatos: <variable> = <variable estructura>.<miembro>; <variable> = <variable estructura>><miembro>; printf( , <variable estructura>.<miembro> ); printf( , <variable estructura>><miembro> ); Algunos ejemplos del uso de la estructura Complejo: float x, y; Complejo z; Complejo *pz; pz = &z; x = z.pr; y = z.pi; . . . printf( \nNmero complejo (%.1f, %.1f), mdulo: %.2f, pz>pr, pz>pi, pz>modulo );

2.3.

Arreglos de estructuras

Se puede crear un arreglo de estructuras tal como se crea un arreglo de otros tipos. Los arreglos de estructuras son idneos para almacenar un archivo completo de empleados, un archivo de inventario, o cualquier otro conjunto de datos que se adapte a un formato de estructura. Mientras que los arreglos proporcionan un medio prctico para almacenar diversos valores del mismo tipo, los arreglos de estructuras le permiten almacenar juntos diversos valores de diferentes tipos, agrupados como estructuras. Muchos programadores utilizan arreglos de estructuras como un mtodo para almacenar datos en un archivo de disco. Se pueden introducir y calcular sus datos de disco en arreglos de estructuras y a continuacin almacenar esas estructuras en memoria. Los arreglos de estructuras proporcionan tambin un medio de guardar datos que se leen del disco.

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

La declaracin de un arreglo de estructuras CD se puede hacer de un modo similar a cualquier arreglo, es decir, CD compactos[100]; asigna un arreglo de 100 elementos denominado compactos. Para acceder a los miembros de cada uno de los elementos estructura se utiliza la notacin de un arreglo. Para inicializar el primer elemento de compactos, por ejemplo, su cdigo debe hacer referencia a los miembros de compactos[0] de la forma siguiente: strcpy( compactos[0].titulo, DARK SIDE OF THE MOON ); strcpy( compactos[0].interprete, PINK FLOYD ); compactos[0].precio = 235.50; strcpy( compactos[0].fecha_compra, 23/04/2009 ); Tambin puede inicializarse un arreglo de estructuras en el punto de la declaracin encerrando la lista de inicializadores entre llaves, { }. Por ejemplo, CD compactos[3] = { DARK SIDE OF THE MOON, PINK FLOYD, 235.50, 23/04/2009 };

2.4.

Uniones

Las uniones son similares a las estructuras en cuanto que agrupa a una serie de variables, pero la forma de almacenamiento es diferente y, por consiguiente, efectos diferentes. Una estructura (struct) permite almacenar variables relacionadas juntas y almacenadas en posiciones contiguas en memoria. Las uniones, declaradas por la palabra reservada union, almacenan tambin miembros mltiples en un paquete; sin embargo, en lugar de situar sus miembros unos detrs de otros, en una unin, todos los miembros se solapan entre s en la misma posicin. El tamao ocupado por una unin se determina as: es analizado el tamao de cada variable de la unin, el mayor tamao de variable ser el tamao de la unin. La sintaxis de una unin es la siguiente: union <nombre> { <tipo> <miembro1>; <tipo> <miembro2>; . . . <tipo> <miembron>; }; Un ejemplo: union PruebaUnion { float Item1; int Item2; };

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

La cantidad de memoria reservada para una unin es igual a la anchura de la variable ms grande. En el tipo union, cada uno de los miembros dato comparten memoria con los otros miembros de la unin. La cantidad total de memoria utilizada por la unin PruebaUnion es de 4 bytes, ya que el elemento float es el miembro dato mayor de la unin. Una razn para utilizar una unin es ahorrar memoria. En muchos programas se deben tener varias variables, pero no necesitan utilizarse todas al mismo tiempo. Considrese la situacin en que se necesitan tener diversas cadenas de caracteres de entrada. Se pueden crear varios arreglos de cadenas de caracteres, tales como las siguientes: char linea_ordenes[80]; char mensaje_error[80]; char ayuda[80]; Estas tres variables ocupan 240 bytes de memoria. Sin embargo, si su programa no necesita utilizar las tres variables simultneamente, por qu no permitirle compartir la memoria utilizando una unin? Cuando se combinan en el tipo unin frases, estas variables ocupan un total de slo 80 bytes. union frases { char linea_ordenes[80]; char mensaje_error[80]; char ayuda[80]; } cadenas, *pc; Para referirse a los miembros de una unin, se utiliza el operador punto (.), o bien el operador >si se hace desde un puntero a unin. As: cadenas.ayuda; cadenas.mensaje_error; pc>mensaje_error;

2.5.

Enumeraciones

Un enum es un tipo definido por el usuario con constantes de nombre de tipo entero. En la declaracin de un tipo enum se escribe una lista de identificadores que internamente se asocian con las constantes enteras 0, 1, 2, etc. Formatos enum <nombre> { <enumerador1>, <enumerador2>, . . . <enumeradorn> };

ESTRUCTURAS DE DATOS

L.I.A. y M.C.C. Pedro Alejo Escarela Rodrguez

En la declaracin del tipo enum pueden asociarse a los identificadores valores constantes en vez de la asociacin que por defecto se hace (0, 1, 2, etc.). Para ello se utiliza el siguiente formato: enum <nombre> { <enumerador1> = <expresin constante1>, <enumerador2> = <expresin constante2>, . . . <enumeradorn> = <expresin constanten> }; Ejemplo: enum Interruptor { APAGADO, ENCENDIDO }; define dos constantes APAGADO y ENCENDIDO de valores iguales a 0 y 1 respectivamente. Los miembros datos de un enum se llaman enumeradores y la constante entera por defecto del primer enumerador es igual a 0. Obsrvese que, al contrario que struct y union, los miembros de un tipo enum se separan por el operador coma (,). El siguiente programa muestra el uso de la enumeracin boolean. El programa lee un texto y cuenta las vocales ledas. La funcin vocal() devuelve TRUE si el carcter de entrada es vocal.
#include <stdio.h> #include <conio.h> #include <ctype.h> enum boolean { FALSE, TRUE }; boolean vocal( char C ); void main() { char c; int nVocales = 0; puts( "\nIngrese un texto. Para terminar: ENTER." ); while( ( c = getchar() ) != '\n' ) { if( vocal( tolower( c ) ) ) nVocales++; } printf( "\n Total de vocales leidas: %d", nVocales ); getch(); } boolean vocal( char c ) { switch( c ) { case 'a': case 'e': case 'i': case 'o': case 'u': return( TRUE ); default: return( FALSE ); } }

Вам также может понравиться