You are on page 1of 31

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

UNIDAD II ESTRUCTURA DE DATOS INCORPORADOS


2.1. ARREGLOS UNIDIMENSIONALES.
DATOS DE TIPO ESTRUCTURADO.- En los lenguajes existen tipos de datos estructurados los cuales nos permiten tener variables que manejan conjunto de valores los cuales pueden ser del mismo tipo o de distinto tipo, con lo cual se puede hacer programas ms eficiente sobre todo cuando se manejan de datos. ARREGLOS (array).- Los arreglos son variables de tipo estructurado que manejan un conjunto de datos del mismo tipo los arreglos pueden ser del tipo unidimensional y multidimensional, a los unidimensionales tambin se les conoce como vectores y a los bidimensionales (matrices) Un arreglo puede definirse como un grupo o una coleccin finita, homognea y ordenada de elementos. Los arreglos pueden ser de los siguientes tipos: De una dimensin. De dos dimensiones. De tres o ms dimensiones. Un arreglo unidimensional es un tipo de datos estructurado que est formado de una coleccin finita y ordenada de datos del mismo tipo. Es la estructura natural para modelar listas de elementos iguales. El tipo de acceso a los arreglos unidimensionales es el acceso directo, es decir, podemos acceder a cualquier elemento del arreglo sin tener que consultar a elementos anteriores o posteriores, esto mediante el uso de un ndice para cada elemento del arreglo que nos da su posicin relativa. Para implementar arreglos unidimensionales se debe reservar espacio en memoria, y se debe proporcionar la direccin base del arreglo, la cota superior y la inferior. REPRESENTACION EN MEMORIA Los arreglos se representan en memoria de la forma siguiente: x : array[1..5] de enteros

Para establecer el rango del arreglo (nmero total de elementos) que componen el arreglo se utiliza la siguiente formula: RANGO = Ls - (Li+1) donde: ls = Lmite superior del arreglo li = Lmite inferior del arreglo

2.1.1.

ARREGLOS UNIDIMENSIONALES FLOTANTES

DE

CARACTERES

(CADENAS),

ENTEROS,

____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 12

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

2.1.1.1 ACCESO A LAS DIFERENTES LOCALIDADES DE UN ARREGLO DE CARACTERES UNIDIMENSIONALES Existen tipos de datos estructurados que permiten tener variables que manejen un conjunto de datos al mismo tiempo (los datos que contiene pueden o no ser del mismo tipo), con lo que se puede manejar grandes cantidades de datos. Los arreglos son variables de tipo estructurado que manejan datos del mismo tipo, los arreglos pueden ser unidimensionales o multidimensionales, a los unidimensionales se les conoce vectores, a los bidimensionales como matrices. Los nombres dados a estas variables deben seguir las reglas de cualquier identificador. Para que estas variables puedan manejar un conjunto de datos deben usar el mismo nombre pero con ndices, a esta forma de direccionamiento se le conoce como localidad del arreglo, en donde cada una de las localidades (de memoria) guardar uno de los valores del conjunto de datos, todos los arreglos utilizados en C++ se direccionan a partir de la localidad cero. Vectores. Se llama as a los arreglos unidimensionales, x[0] x[1] x[2] x[3] Direccionamiento (localidades del arreglo)

Para declarar un arreglo unidimensional se deber poner el tipo de datos que maneja el arreglo, el nombre y entre corchetes cuadrados con un nmero entero (o una constante) la cantidad total de localidades, por ejemplo: int x[20]; float y[10]; char a[15];

Los arreglos pueden inicializarse a la vez que se declaran, esto es, si enseguida de la declaracin del arreglo se pone el signo igual y entre llaves los valores con los cuales s inicializa el arreglo: int z[5] = {1,4,0,10,-2};char c[6] = {a,e,i,o,u,\0}; Si se deseara inicializar con cero un arreglo numrico, se puede poner entre llaves un cero y de esta manera automticamente todas las localidades tomarn el valor de cero, esto se muestra a continuacin: int w[5] ={0}; Para manejar adecuadamente este tipo de variables es importante recordar que junto con el nombre se debe indicar a qu localidad del arreglo se est haciendo mencin, y de esta forma se puede usar como cualquier variable de tipo simple por ejemplo. y = x[3] + 3* x[2]; x[1] = 7; scanf ( %d ,&x[2]); Cuando se desea leer la totalidad de un arreglo se debe hacer mediante una estructura de repeticin, se recomienda el uso del for por ser la estructura que incluye ms posibilidades en s misma, aunque sin descartar las otras, un ejemplo de cmo se realiza esto se pone a continuacin: for (i=0; i <=9; i++) {
____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 13

x[0] = x[4]/4; printf(\n %d,x[3]);

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

printf(\n D el valor de la localidad(%d):,i); scanf(%d, &x[i]); } Para desplegar el contenido de un arreglo tambin se emplea una estructura de control, for, preferentemente, como se muestra: for(i=0; i<=9; i++) printf(\n %d, x[i]); CADENA DE CARACTERES (STRING).- Un caso especial de los arreglos unidimensionales ser l emplearlos para poder manejar variables de cadena es decir que si declara un arreglo de caracteres, con las funciones adecuadas se puede manejar como una sola cadena, las funciones a emplearse sern gets( ) y puts( ) con las cuales respectivamente se leer la cadena o se desplegara en pantalla, en el siguiente ejemplo se supondr que se declaro un arreglo unidimensional de caracteres el cual se usara para poder manejarse como cadena, en las cadenas se deber considerar una localidad de la cadena para el terminador nulo ('\0') pues al leer la cadena esto es agregado automticamente por lo que si la cadena a almacenarse en el arreglo requiere de la totalidad de las localidades es aconsejable declararlo con mas localidades pues el terminador nulo lo Tomara la ultima, recortando la cadena a leer, tambin se requiere considerar que las funciones gets( ) y puts( ) al emplearse para leer o desplegar la cadena solo usara el nombre del arreglo pues al usarse como cadena no se requiere de los corchetes pues en ese caso se estara empleando como arreglo de caracteres y no como una cadena. char nombre[20]; gets(nombre); puts(nombre);

Para poder manipular cadena en los programas en turbo C esto se deber de hacer mediante las funciones de cadena de las librera string.h si emplean esas funciones en un programa se deber de declarar la librera mediante la palabra reservada include, a continuacin se darn algunas de estas funciones ( las ms comunes de emplearse) y su sintaxis. char strcpy (char *dest, const char src) char strcat (char *dest, const char src) int strcmp (const char *sl, const char *s2)
s1<s2 Copia src dest despus de que el terminador nulo ha sido movido Concadena cadenas. Compara dos cadenas devuelve o si son iguales>0 si s1>s2 y <0 si Determina la cantidad de caracteres contenida en una cadena. Convierte la cadenas a minsculas Convierte la cadenas a maysculas.

size_t strlen(const char *s) char strlwr(const char *s) char strupr(const char *s)

A continuacin se dan algunos ejemplos de programas que utilizan arreglos unidimensionales. 1.- Programa que inicializa un arreglo de enteros a ceros, lo lee y lo despliega #include<stdio.h> #include<conio.h>

____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 14

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

void main ( ) { int i,x[10]={0}; clrscr( ); gotoxy(6,3); printf(Prctica # 7:); gotoxy(6,5); printf(ARREGLOS UNIDIMENSIONALES ); gotoxy(6,7); printf(Programa que inicializa un arreglo de enteros a ceros, lo lee y lo despliega); printf(los valores de inicializacion y los leidos en pantalla \n\n\n); for(i = 0; i<10; i++) printf(%3d,x[i]); printf(\n\n); for (i=0; i<10; i++) { printf(\n Teclee el valor de la %d localidad:, i); scanf(%d,&x[i]); } printf(\n\n) for (i = 0; i<10; i++) printf(%3d,x[i]); getch(); }

2.- Este programa inicializara un arreglo de enteros a cero, despus leer n localidades del arreglo para ordenarlo y despus desplegarlo y en orden, para la ordenacin se empleara el mtodo de la burbuja. #include<stdio.h> #include<conio.h> #define size 100 void main ( ) { int n,i,j,x[size]={0}; clrscr( );
____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 15

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

gotoxy(6,3); printf(Programa que lee arreglo de enteros, lo ordena y lo); gotoxy(6,5); printf(\n despliega ordenado en pantalla:); gotoxy(6,7); printf(Cuantos datos va a leer en el arreglo); scanf(%d,&n); for (i=1; i<n; i++) { printf(\n Teclee el valor de la %d:, i); scanf(%d,&x[i]); } clrscr(); for (i = 1; i<n; i++) for (j = 1; j<n; j++) if(x[j]>x[j+1]) { x[0]=x[j]; x[j]=x[j+1]; x[j+1]=x[0]; } printf(\n\n); for (i=1; i<=n; i++) printf( %3d:, x[i]); getch(); }

2.2. ARREGLOS BIDIMENSIONALES


2.2.1. ARREGLOS BIDIMENSIONALES, MATRICES

Arreglos bidimensionales (matrices). Para entender cmo funcionan este tipo de arreglos debemos imaginar que se trata de una caja (una cuadrcula en la cual cada divisin es una localidad del arreglo) y similarmente a los vectores se deben direccionar con ndices, pero en este caso se trata de dos ndices, es decir una fila y una columna, por lo que al declararla se debe poner el tipo de datos que va a manejar, el nombre y la totalidad de filas y columnas cada una entre corchetes, int x[3][4]; int A[4][3]; float x[4][6]; char c[10]20];

En el ejemplo utilizado tenemos una matriz de 3 filas (de la 0 a la 2) y cuatro columnas(de la 0 a la 3), dando un total de 12 casillas que tendrn el nombre x y manejarn datos de tipo entero. Podemos decir que el contenido de cada una de las casillas representa una localidad de memoria y el contenido entre ellas es independiente, con lo nico que debe cumplirse que todos loa valores guardados
____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 16

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

sean del mismo tipo de datos que se declar. Las casillas se direccionan a partir de la localidad 0,0, como se muestra: x[0][0] x[1][0] x[2][0] x[0][1] x[1][1] x[2][1] x[0][2] x[1][2] x[2][2] x[0][3] x[1][3] x[2][3]

La manera de utilizar estas variables ser siempre acompaadas del direccionamiento necesario para poder diferenciar cada localidad, esto es, no basta con el nombre del arreglo sino que se debe indicar entre corchetes la fila y columna de cada localidad utilizada. Las casillas pueden formar parte de expresiones, asignrsele valores, leer o desplegar datos en forma individual de cada casilla del arreglo, como se muestra A[2][0]=6; A[2][1]3*c + 2*a; printf(\n %d,A[2][2]);scanf(%d,&A[2][2]);

Algunas veces se requiere inicializar los arreglos, leerlos o desplegar la totalidad del arreglo, la inicializacin se puede realizar de diferentes formas: int A[4][3]=0, es una de ellas Se pueden utilizar estructuras de repeticin anidadas para las diferentes funciones. Para leer el contenido de todas las localidades: for(i=0;i<3;i++) for(j=0;j<4;j++) { printf(\n D el valor de la localidad %d%d,i,j); scanf(%d&A[i][j]; } De igual manera se usa una estructura de repeticin anidada para desplegar el contenido del arreglo: for(i=0;i<3;i++) for(j=0;j<4;j++) printf(\n %d,A[i][j]); Los arreglos pueden manejar caracteres o cadenas de caracteres y se utilizan en C muy comnmente. #include<stdio.h> #include<conio.h> #include<time.h> #define NULL 0 #define MZ 5 #define MM 6 int i,j; float acum,zona[MZ],edad [MZ][MM]; char enter; void main() {
____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 17

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

time_t t; t=time(NULL); textbackground(BLUE);textcolor(YELLOW); clrscr(); gotoxy(25,1); printf("Fecha:%s",ctime(&t)); gotoxy(6,5); printf("Manejo de ARREGLOS BIDIMENSIONALES"); gotoxy(6,7); printf("1=Zona Norte;2=Zona Sur;3=Zona Este;4=Zona Oeste;5=Zona Centro"); printf("\n\n\n"); for(i=0;i<MZ;i++) for(i=0;i<MZ;i++) zona[i]=0; for(j=0;j<MM;j++) edad[i][j]=0;

for(i=0;i<MZ;i++) for(j=0; j<MM;j++) { printf("Teclee de la zona(%d),la edad(%d):",i+1,j+1); scanf("%f%c",&edad[i][j],&enter); } for(i=0;i<MZ;i++) { acum=0; for(j=0;j<MM;j++) acum=acum + edad[i][j]; zona[i]=acum/MM; } printf("1=Zona Norte; 2=Zona Sur; 3=Zona Este, 4=Zona Oeste; %=Zona Centro"); for(i=0;i<MZ;i++) printf("\n La zona (%d),tiene un promedio de edad de:%10.4f,i+1,zona[i]"); printf("\n\nPresione cualquier letra para continuar...");getch(); textbackground(BLACK);textcolor(WHITE);clrscr(); } 2.3. REGISTROS . 2.3.1. REGISTROS Y PUNTEROS 2.3.1.1 ACCESOS A LOS CAMPOS DE UN REGISTRO MEDIANTE PUNTEROS 2.3.2. ARREGLOS DE REGISTROS, REGISTROS DE REGISTROS Los conjuntos de datos se organizan frecuentemente en la jerarqua de campos, registros y archivos. Concretamente un registro lo constituyen un conjunto de elementos relacionados entre s cada uno de los cuales recibe el nombre de CAMPO o ATRIBUTO y un archivo un conjunto de registros similares. Cada elemento a la vez elemento. Aquellos elementos que son individuales reciben el nombre de elementos simples, tomos o escalares. Aunque un registro es un conjunto de elementos se diferencia de un arreglo lineal por lo siguiente: a) Un registro puede ser un conjunto de datos no homogneos. b) Los elementos de un registro son referencia a travs del nombre de cada atributo por lo que no existe un orden natural de los elementos. Los elementos de un registro forman una estructura jerrquica que puede describirse a travs de nmeros de nivel. ESTUDIANTE NOMBRE SEXO Fecha de nacimiento
EDAD

____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 18

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

PATERNO

MATERNO

NOMBRES

Da

Mes Ao

1. ESTUDIANTE 2. NOMBRE 3. APELLIDO PATERNO 3. APELLIDO MATERNO 3. NOMBRES 2. SEXO 2. FECHA DE NACIMIENTO 3. DIA 3. MES 3. AO 2. EDAD

ESTRUCTURA DE DATOS (struct).- Otro de los tipos de datos estructurado que existen en turbo C son las estructuras (REGISTROS) las cuales se puede manejar al igual que los arreglos un conjunto de datos pero a diferencia de estos pueden ser de distinto, la manera de declarar estas estructuras es fuera de todo bloque del programa principal, se constituye igual que un registro el cual se compone de un conjunto de datos, a continuacin se muestra como debe declararse una estructura. struct <nombre> { campo 1; campo 2; campo n; } variables; De lo anterior podemos decir que nombre se refiere al nombre que se le va a dar al tipo de estructura a crear la cual variara de aplicacin es decir cuantos campos empleara y de que tipo ser cada uno de estos por lo que el nombre equivale a declarar un tipo especifico de datos, las variables se refiere a que una vez definida la estructura y sus campos, estos debern manejarse mediante una variable que corresponda a ese tipo, cada campo deber declararse como se declaran la variables en un programa, a continuacin se da un ejemplo de una estructura. struct datos_per { char nom[20]; int edad; int estatura; } d,x[5];
____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 19

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

En el ejemplo anterior podemos observar que la estructura se nombra como datos_per que contiene tres campos uno de ellos es una cadena de caracteres (un arreglo unidimensional de caracteres), y dos campos de enteros para manejar datos numricos, finalmente las variables a emplear por esta estructura y que corresponden al tipo datos_per son dos, la primera d, solo puede manejar un solo registro es decir nombre, una edad y una estatura, y la segunda equivale a un arreglo de registros es decir pueden manejar un conjunto de registros, por lo que la manera de leer o desplegar o asignar valores a los campos de un registro o estructura se muestra a continuacin. strcpy(d.nom, Pedro); printf (%s,d.nom); scanf(%s,&d.nom); d.edad=40; printf (%d,d.edad); scanf(%d,&d.edad); d. estatura=170; printf (%d,d.estatura); scanf(%d,&d.esatura);

Par el caso en el cual la variable sea un arreglo es decir un arreglo de registros se deber asignar especificando el registro al que se refiere de la totalidad de los existentes en el arreglo (localidad), si se manejan de otra manera se deber de hacer usando estructuras de control de repeticin. strcpy(x[2],nom,Raul) for (i=0;i<5;i++) x[i ].edad=0 for (i=0;i<5;i++) printf(%d,x[i ].estatura); for (i=0;i<5;i++) { printf(\n De ala edad del registro %d:,i); scanf(%d, x[i ].edad); Una variante de uso de estas estructuras es que al declarar la estructura no se declaran las variables y esta s se declaran posteriormente dentro del programa principal. struct datos_per { char nom[20]; int edad; int estatura; } d,x[5]; #include <stdio.h> #include <conio.h> void main() { struct datos per d, x[5]; printf(\n %s,x[0].nom); scanf(%s,&x[3].nom);

____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 20

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

El siguiente ejemplo nos muestra como debe declararse, leerse y desplegar en pantalla una estructura de datos primeramente solo se har con una estructura sencilla. #include <stdio.h> #include <conio.h> struct agenda { char nom[20]; char tel [15]; int edad; } a; void main() { clrscr(); printf("PROGRAMA que registra el nombre edad y telfono de 1 persona"); printf("\n\n De el nombre "); scanf("%s",&a.nom); printf(" Da la edad "); scanf("%d",&a.edad); printf(" De el telfono "); scanf("%s",&a.tel); printf("\n Los datos dela persona son:%s %d %s ",a.nom,a.edad,a.tel); getch(); } El segundo ejemplo har lo mismo que el programa anterior pero a diferencia del mismo es que la variable de la estructura ser un arreglo que corresponda al tipo de l estructura, tambin se usara la variante de declararla dentro de la funcin. #include <stdio.h> #include <conio.h> struct agenda { char nom[20]; char tel [15]; int edad; } void main() { int i; struct agenda b[5]; clrscr(); for (i=0;i<=4;i++) { printf("PROGRAMA que registra el nombre edad y telfono de 5 personas"); printf("\n\n De el nombre %d: ",i); scanf("%s",&b[i].nom); printf(" Da la edad %d: ",i); scanf("%d",&b[i].edad);
____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 21

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

printf(" De el telfono %d: ",i); scanf("%s",&b[i].tel); } getch(); clrscr(); for (i=0;i<=4;i++) printf("\nLos datos dela persona son:%20s %2d %15s ",b[i].nom,b[i].edad,b[i].tel); getch(); }

INTRODUCCION A LOS PUNTEROS Sea DATOS un arreglo decimos que la variable p es un puntero si p "apunta" hacia un elemento de DATOS es decir si p contiene la direccin de un elemento de DATOS de forma anloga decimos que un arreglo ptr es un arreglo de punteros si cada elemento de ptr es un puntero. Los punteros y los arreglo de punteros se utilizan para facilitar el procesamiento de informacin almacenada en DATOS. Supongamos que una organizacin divide la lista de sus miembros en cuatro grupos donde cada grupo contiene una lista alfabtica de los miembros que residen en una zona determinada. Supongamos que la figura siguiente representa tal lista. Obsrvese que la lista contiene veintin personas que cada grupo se compone de cuatro, nueve, dos y seis personas. GRUPO 1 SILVIA MARIO ALBERTO ORLANDO GRUPO 2 MARTHA OMAR DAVID AUSENCIO GERMAN RUBEN MIGUEL ANTONIO SALVADOR GRUPO 3 TERESA JUAN GRUPO 4 MAYTE FRANCISCO LAURA JULIA ALEJANDRA MAURICIO

Supongamos que queremos almacenar la lista en memoria, pero diferenciando los distintos grupos en que esta dividido. Una forma de hacerlo es utilizando un arreglo bidimensional de 4xn donde cada fila contiene un grupo o tambin un arreglo de nx4 donde cada columna contiene 1. Aunque esta estructura nos permite llegar a cada elemento individualmente se desperdicia mucho espacio cuando los grupos varan mucho. Concretamente los datos necesitan un arreglo de 4x9 o de 4x4 es decir 36 elementos como mnimo para almacenar los 21 nombres. Otra forma de almacenar la lista en memoria lo representamos en la siguiente figura. 1 2 3 4 5 6 SILVIA MARIO ALBERTO ORLANDO MARTHA OMAR

GRUPO 1

____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 22

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

DAVID AUSENCIO GERMAN RUBEN MIGUEL ANTONIO SALVADOR TERESA JUAN MAYTE FRANCISCO LAURA JULIA ALEJANDRA MAURICIO n-1 n

GRUPO 2

GRUPO 3

GRUPO 4

Es decir la lista se almacena en un arreglo lineal colocando un grupo detrs del otro. Claramente este mtodo es muy eficiente en cuanto a la utilizacin del espacio tambin podemos procesar fcilmente la lista completa (podemos imprimir los nombre de la lista). Por el no existe para imprimir solo los miembros de algn grupo. Una versin modificada de este mtodo muestra en la siguiente figura. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 SILVIA MARIO ALBERTO ORLANDO $$$ MARTHA OMAR DAVID AUSENCIO GERMAN RUBEN MIGUEL ANTONIO SALVADOR $$$ TERESA JUAN $$$ MAYTE FRANCISCO LAURA JULIA ALEJANDRA MAURICIO $$$

GRUPO 1

GRUPO 2

GRUPO 3

GRUPO 4

____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 23

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

26 27

n-1 n

En este caso los nombre se almacenan secuencialmente pero intercalando entre cada grupo una marca para indicar el final de cada grupo.

Las dos estructuras anteriores pueden modificarse fcilmente de tal forma que cada grupo puede indexarse esto puede llevarse acabo utilizando un arreglo de punteros GRUPO que contiene las posiciones de los diferentes grupos o ms concretamente, la posicin del primer elemento de cada grupo. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 SILVIA MARIO ALBERTO ORLANDO MARTHA OMAR DAVID AUSENCIO GERMAN RUBEN MIGUEL ANTONIO SALVADOR TERESA JUAN MAYTE FRANCISCO LAURA JULIA ALEJANDRA MAURICIO n-1 n

GRUPO 1

GRUPO 2

1 2 3 4 5

1 5 14 16 22

GRUPO 3

GRUPO 4

Obsrvese que el GRUPO[L] y GRUPO[L+]-1 contiene los elementos primero y ultimo del grupo L. Los punteros en el Lenguaje C , son variables que " apuntan " , es decir que poseen la direccin de las ubicaciones en memoria de otras variables, y por medio de ellos tendremos un poderoso mtodo de acceso a todas ellas . Quizs este punto es el ms conflictivo del lenguaje , ya que muchos programadores en otros idiomas , y novatos en C , lo ven como un mtodo extrao al menos desacostrumbrado , lo que les produce un cierto rechazo . Sin embargo , y en la medida que uno se va familiarizando con ellos , se convierten en la herramienta ms cmoda y directa para el manejo de variables complejas , argumentos , parmetros , etc , y se empieza a preguntar como es que hizo para programar hasta aqu , sin ellos . La respuesta es que no lo ha hecho , ya que los hemos usado en forma encubierta , sin decir lo que
____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 24

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

eran . ( Perdn por el pequeo engao ). Veamos primero , como se declara un puntero : tipo de variable apuntada *nombre_del_puntero ; int *pint ; double *pfloat ; char *letra , *codigo , *caracter ; En estas declaraciones slo decimos al compilador que reserve una posicin de memoria para albergar la direccin de una variable , del tipo indicado , la cual ser referenciada con el nombre que hayamos dado al puntero . Obviamente , un puntero debe ser inicializado antes de usarse , y una de las eventuales formas de hacerlo es la siguiente: int var1 ; int *pint ; pint = &var1 ; /* declaro ( y creo en memoria ) una variable entera ) */ /* " entera */ " " " " un puntero que contendr la direccin de una variable

/* escribo en la direccin de memoria donde est el puntero la direccin de la variable entera */

Como habiamos anticipado en captulos anteriores " &nombre_de_una_variable " implica la direccin de la misma . Si se pregunta porque no se usaron otros smbolos en vez de & y * , que se confunden con la Y lgica de bits y el producto , ..... consuelese pensando que yo tambin me hice siempre esa pregunta . De cualquier manera es siempre obvio , en el contexto del programa el uso de los mismos . Esquematicamente , lo que hemos hecho se puede simbolizar de la siguiente manera : donde dentro del recuadro est el contenido de cada variable . Pint xxxxxx Direccin de var1 yyyyyy (posicin de memoria ocupada por el puntero ) valor contenido por var1 xxxxxx (posicin de memoria ocupada por la variable)

En realidad , como veremos ms adelante , en la declaracin del puntero , est implicita otra informacin : cual es el tamao (enbytes) de la variable apuntada. El smbolo & , direccin , puede aplicarse a variables , funciones , etc , pero n a constantes expresiones , ya que stas no tienen una posicin de memoria asignada. La operacin inversa a la asignacin de un puntero , de referenciacin del mismo , se puede utilizar para hallar el valorcontenido por la variable apuntada . As por ejemplo sern expresiones equivalentes : y = var1 ; y = *pint ;
____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 25

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

printf("%d" , var1 ) ; printf("%d" , *pint) ; En estos casos , la expresin " *nombre_del_puntero " , implica " contenido de la variable apuntada por el mismo " . Veamos un corto ejemplo de ello : #include <stdio.h> main() { char var1 ; char *pchar; pc = &var1 ; for (var1 = 'a'; var1 <<= 'z'; var1++) printf("%c", *pchar) ; return 0 ; } Vemos ac , que en el FOR se incrementa el valor de la variable , y luego para imprimirla usamos la dereferenciacin de su puntero. El programa imprimir las letras del abecedario de la misma manera que lo habra hecho si la sentencia del printf() huiera sido, printf("%c" , var1 ) . Hay un error , que se comete con bastante frecuencia , y es cargar en la direccin apuntada por un puntero a un tipo dado de variable , el contenido de otro tipo de las mismas , por ejemplo : double d = 10.0 ; int i = 7 , *pint ; pint = &i ; *pint = 10 ; *pint = d ; pint = &d ; pint = 4358 ; /* correcto,equivale a asignar a i el valor 10 */ ; /* ERROR se pretende cargar en una variable entera un valor double */ /* INCORRECTO se pretende apuntar a una variable double con un puntero declarado como apuntador a int */ /* ?????? */ /* imprimimos el valor de la variable apuntada */ /*una variable del tipo caracter */ /* un puntero a una variable del tipo caracter */ /*asignamos al puntero la direccion de la variable */

El primer error , la asignacin de un double , produce la prdida de informacin dada por la conversin automtica de tipo de variable , ya vista anteriormente , el segundo produce un llamado de atencin rotulado como " asignacin sospechosa de un pointer " . Resumiendo , las variables constantes cargadas por dereferenciacin de un puntero , deben coincidir en tipo con la declaracin de aquel .

____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 26

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

La asignacin de una constante a un pointer , y no a la variable apuntada por l , es un serio error , ya que debe ser el compilador , el encargado de poner en l el valor de la direccin , aquel as lo declara dando un mensaje de " conversin de puntero no transportable " . Si bien lo compila , ejecutar un programa que ha tenido esta advertencia es similar a jugar a la ruleta rusa , puede "colgarse" la mquina lo que es peor destruirse involuntariamente informacin contenida en un disco , etc. Hay un slo caso en el que esta asignacin de una constante a un puntero es permitida , muchas funciones para indicar que no pueden realizar una accin que se ha producido un error de algun tipo , devuelven un puntero llamado "Null Pointer" , lo que significa que no apunta a ningun lado vlido , dicho puntero ha sido cargado con la direccin NULL ( por lo general en valor 0) , as la asignacin : pint = NULL ; es vlida y permite luego operaciones relacionales del tipo if( pint ) ..... if( print != NULL) para convalidar la validez del resultado devuelto por una funcin . Una advertencia : si bien volveremos ms adelante sobre este tema , debemos desde ahora tener en cuenta que los punteros no son enteros , como parecera a primera vista , ya que el nmero que representa a una posicin de memoria , s lo es . Debido al corto alcance de este tipo de variable , algunos compiladores pueden , para apuntar a una variable muy lejana , usar cualquier otro tipo , con mayor alcance que el antedicho . PUNTEROS Y ARRAYS Hay una relacin muy cercana entre los punteros y los arrays . Y vimos previamente que el designador ( nombre de un array) era equivalente a la direccin del elemento [0] del mismo . La explicacin de sto es ahora sencilla : el nombre de un array , para el compilador C , es un PUNTERO inicializado con la direccin del primer elemento del array . Sin embargo hay una importante diferencia entre ambos , que haremos notar ms abajo. Veamos algunas operaciones permitidas entre punteros : ASIGNACION float var1 , conjunto[] = { 9.0 , 8.0 , 7.0 , 6.0 , 5.0 ); float *punt ; punt = conjunto ; var1 = *punt ; *punt = 25.1 ; Es perfectamente vlido asignar a un puntero el valor de otro , el resultado de sta operacin es cargar en el puntero punt la direccin del elemento [0] del array conjunto , y posteriormente en la variable var1 el valor del mismo (9.0) y para luego cambiar el valor de dicho primer elemento a 25.1 . Veamos cual es la diferencia entre un puntero y el denominador de un array : el primero es una VARIABLE , es decir que puedo asignarlo , incrementarlo etc , en cambio el segundo es una CONSTANTE , que apunta siempre al primer elemento del array con que fu declarado , por lo que su contenido NO PUEDE SER VARIADO . Si lo piensa un poco , es lgico , ya que "conjunto" implica la direccin del elemento conjunto [0] , por lo que , si yo cambiara su valor , apuntara a otro lado dejando de ser , "conjunto" . Desde este punto de vista , el siguiente ejemplo nos muestra un tipo de error bastante frecuente: ASIGNACION ERRONEA /* equivalente a hacer : punt = &conjunto [0] */

____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 27

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

int conjunto[5] , lista[] = { 5 , 6 , 7 , 8 , 0 ) ; int *apuntador ; apuntador = lista ; conjunto = apuntador; lista = conjunto ; /* correcto */ /* ERROR ( se requiere en Lvalue no constante ) */

/* ERROR ( idem ) */ /* ERROR no se puede aplicar el operador & (direccin) a una constante */

apuntador = &conjunto

Veamos ahora las distintas modalidades del incremento de un puntero : INCREMENTO O DECREMENTO DE UN PUNTERO int *pint , arreglo_int[5] ; double *pdou , arreglo_dou[6] ; pint = arreglo_int ; pdou = arreglo_dou ; pint += 1 ; pdou += 1 ; pint++ ; pdou++ ; /* pint apunta a arreglo_int[0] */ /* pdou apunta a arreglo_dou[0] */ /* pint apunta a arreglo_int[1] */ /* pdou apunta a arreglo_dou[1] */ /* pint apunta a arreglo_int[2] */ /* pdou apunta a arreglo_dou[2] */

Hemos declarado y asignado dos punteros , uno a int y otro a double , con las direcciones de dos arrays de esas caracteristicas . Ambos estarn ahora apuntando a los elementos [0] de los arrays . En las dos instrucciones siguientes incrementamos en uno dichos punteros . adonde apuntaran ahora ?. Para el compilador , stas sentencias se leen como : incremente el contenido del puntero ( direccin del primer elemento del array ) en un nmero igual a la cantidad de bytes que tiene la variable con que fu declarado . Es decir que el contenido de pint es incrementado en dos bytes (un int tiene 2 bytes ) mientras que pdou es incrementado 8 bytes ( por ser un puntero a double ), el resultado entonces es el mismo para ambos , ya que luego de la operacin quedan apuntando al elemento SIGUIENTE del array , arreglo_int[1] y arreglo_dou[1] . Vemos que de sta manera ser muy facil "barrer" arrays , independientemente del tamao de variables que lo compongan, permitiendo por otro lado que el programa sea transportable a distintos hardwares sin preocuparnos de la diferente cantidad de bytes que pueden asignar los mismos , a un dado tipo de variable. De manera similar las dos instrucciones siguientes , vuelven a a incrementarse los punteros , apuntando ahora a los elementos siguientes de los arrays.

____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 28

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

Todo lo dicho es aplicable , en identica manera , al operador de decremento -- . ARITMETICA DE DEREFERENCIA Debido a que los operadores * y ++ -- tienen la misma precedencia y se evaluan de derecha a izquierda , y los parntesistienen mayor precedencia que ambos , muchas operaciones que los utilizan en conjunto a todos estos operadores , pueden parecer poco claras y dar origen a un sinnmero de errores , (revise un poco la TABLA 13 del captulo 3 ) analicmoslas detalladamente , partiendo de : int *p , a[] = { 0 , 10 , 20 , 30 , 40 , 50 , 60 , 70 , 80 , 90 } ; int var ; p=a; A partir de aqu , el puntero est apuntando a a[0] . Veamos las distintas variantes que puede tener la siguiente instruccin: *p = 27 ; La ms sencilla de las opciones , simplemente asignamos al elemento apuntado por p ( a[0] ) un valor constante . Veamos la inversa de ella: var = *p ; var sera asignada al valor 0 (contenido de a[0]) , y p seguira apuntando al mismo elemento. Que hubiera pasado, si en vez de ello se hubiera escrito: var = *( p + 1 ) ; ac podramos traducir el sentido de la operacin como : cargue var con el contenido del elemento siguiente al apuntado por p ( a[1] ) . Lo interesante de remarcar ac es que p , en s mismo , NO VARIA Y LUEGO DE ESTA SENTENCIA SEGUIRA APUNTANDO A a[0] . De la misma forma : var = *( p + 3 ) asignar 30 a var , sin modificar el contenido de p . En cambio la expresin : var = *( p++ ) ; podemos leerla como : asigne a var el valor de lo apuntado por p y LUEGO incremente ste para que apunte al proximo elemento . As en var quedara 0 ( valor de a[0] ) y p apuntara finalmente a a[1] . Si en vez de sto hubieramos preincrementado a p tendramos : var = *( ++p ) ; la que puede leerse como : apunte con p al prximo elemento y asigne a var con el valor de ste . En este caso var sera igualada a 10 ( a[1] ) y p quedara apuntando al mismo . En las dos operaciones anteriores los parntesis son superfluos ya que al analizarse los operadores de derecha a izquierda , dara lo mismo escribir : var = *p++ ; var = *++p ; /* sintcticamente igual a var = *(p++) */ /* " " " var = *(++p) */

____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 29

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

ARITMETICA DE PUNTEROS La aritmtica ms frecuentemente usada con punteros son las sencillas operaciones de asignacin , incremento decremento y dereferenciacin . Todo otro tipo de aritmtica con ellos est prohibida es de uso peligroso poco transportable . Por ejemplo no est permitido , sumar , restar , dividir , multiplicar , etc , dos apuntadores entre s . Lo cual si lo pensamos un poco es bastante lgico , ya que de nada me servira sumar dos direcciones de memoria , por ejemplo . Otras operaciones estan permitidas , como la comparacin de dos punteros , por ejemplo ( punt1 == punt2 ) ( punt1 <punt2 ) sin embargo este tipo de operaciones son potencialmente peligrosas , ya que con algunos modelos de pointers pueden funcionar correctamente y con otros no . PUNTEROS Y VARIABLES DINAMICAS Recordemos lo expresado sobre el mbito existencia de las variables , la menos duradera de ellas era la del tipo local a una funcin , ya que naca y mora con sta . Sin embargo , esto es algo relativo , en cuanto a la funcin main() , ya que sus variables locales ocuparn memoria durante toda la ejecucin del programa. Supongamos un caso tpico , debemos recibir una serie de datos de entrada , digamos del tipo double , y debemos procesar segn un determinado algoritmo a aquellos que aparecen una ms veces con el mismo valor . Si no estamos seguros de cuantos datos van a ingresar a nuestro programa , pondremos alguna limitacin , suficientemente grande a los efectos de la precisin requerida por el problema , digamos 5000 valores como mximo , debemos definir entonces un array de doubles capaz de albergar a cinco mil de ellos , por lo que el mismo ocupar del orden de los 40 k de memoria . Si definimos este array en main() , ese espacio de memoria permanecer ocupado hasta el fn del programa , aunque luego de aplicarle el algoritmo de clculo ya no lo necesitemos ms , comprometiendo seriamente nuestra disponibilidad de memoria para albergar a otras variables . Una solucin posible sera definirlo en una funcin llamada por main() que se ocupara de llenar el array con los datos , procesarlos y finalmente devolviera algn tipo de resultado , borrando con su retorno a la masiva variable de la memoria . Sin embargo en C existe una forma ms racional de utilizar nuestros recursos de memoria de manera conservadora . Los programas ejecutables creados con estos compiladores dividen la memoria disponible en varios segmentos , uno para el cdigo ( en lenguaje mquina ) , otro para albergar las variables globales , otro para el stack ( a travez del cual se pasan argumentos y donde residen las variables locales ) y finalmente un ltimo segmento llamado memoria de apilamiento amontonamiento (Heap ) . El Heap es la zona destinada a albergar a las variables dinmicas , es decir aquellas que crecen ( en el sentido de ocupacin de memoria ) y decrecen a lo largo del programa , pudiendose crear y desaparecer (desalojando la memoria que ocupaban) en cualquier momento de la ejecucin . Veamos cual sera la metodologa para crearlas ; supongamos primero que queremos ubicar un nico dato en el Heap , definimos primero un puntero al tipo de la variable deseada : double *p ; notemos que sta declaracin no crea lugar para la variable , sino que asigna un lugar en la memoria para que posteriormente se guarde ah la direccin de aquella Para reservar una cantidad dada de bytes en el
____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 30

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

Heap , se efectua una llamada a alguna de las funciones de Librera , dedicadas al manejo del mismo . La ms tradicional es malloc() ( su nombre deriva de memory allocation ) , a esta funcin se le d como argumento la cantidad de bytes que se quiere reservar , y nos devuelve un pointer apuntando a la primer posicin de la "pila" reservada . En caso que la funcin falle en su cometido ( el Heap est lleno ) devolvera un puntero inicializado con NULL . p = malloc(8) ; ac hemos pedido 8 bytes ( los necesarios para albergar un double ) y hemos asignado a p el retorno de la funcin , es decir la direccin en el Heap de la memoria reservada. Como es algo engorroso recordar el tamao de cada tipo variable , agravado por el hecho de que , si reservamos memoria de esta forma , el programa no se ejecutar correctamente , si es compilado con otro compilador que asigne una cantidad distinta de bytes a dicha variable , es ms usual utilizar sizeof , para indicar la cantidad de bytes requerida : p = malloc( sizeof(double) ) ; En caso de haber hecho previamente un uso intensivo del Heap , se debera averiguar si la reserva de lugar fu exitosa: if( p == NULL ) rutina_de_error() ; si no lo fu estas sentencias me derivan a la ejecucin de una rutina de error que tomar cuenta de este caso . Por supuesto podra combinar ambas operaciones en una sola , if( ( p = malloc( sizeof(double) ) ) == NULL ) { printf("no hay mas lugar en el Heap ..... Socorro !!" ) ; exit(1) ; } se ha reemplazado aqu la rutina de error , por un mensaje y la terminacin del programa , por medio de exit() retornando un cdigo de error . Si ahora quisiera guardar en el Heap el resultado de alguna operacin , sera tan directo como, *p = a * ( b + 37 ) ; y para recuperarlo , y asignarselo a otra variable bastara con escribir : var = *p ;

PUNTEROS A STRINGS No hay gran diferencia entre el trato de punteros a arrays , y a strings , ya que estos dos ltimos son entidades de la misma clase . Sin embargo analicemos algunas particularidades . As como inicializamos un string con un grupo de caracteres terminados en '\0' , podemos asignar al mismo un puntero :
____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 31

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

p = "Esto es un string constante " ; esta operacin no implica haber copiado el texto , sino slo que a p se le ha asignado la direccin de memoria donde reside la "E" del texto . A partir de ello podemos manejar a p como lo hemos hecho hasta ahora . Veamos un ejemplo #include <stdio.h> #define TEXTO1 " Hola , como " #define TEXTO2 "le va a Ud. ? " main() { char palabra[20] , *p ; int i ; p = TEXTO1 ; for( i = 0 ; ( palabra[i] = *p++ ) != '\0' ; i++ ) ; p = TEXTO2 ; printf("%s" , palabra ) ; printf("%s" , p ) ; return 0 ; } Definimos primero dos strings constantes TEXTO1 y TEXTO2 , luego asignamos al puntero p la direccin del primero , y seguidamente en el FOR copiamos el contenido de ste en el array palabra , observe que dicha operacin termina cuando el contenido de lo apuntado por p es el terminador del string , luego asignamos a p la direccin de TEXTO2 y finalmente imprimimos ambos strings , obteniendo una salida del tipo : " Hola , como le va a UD. ? " ( espero que bien ) . Reconozcamos que esto se podra haber escrito ms compacto, si hubieramos recordado que palabra tambien es un puntero y NULL es cero , as podemos poner en vez del FOR while( *palabra++ = *p++ ) ; Vemos que aqu se ha agregado muy poco a lo ya sabido , sin embargo hay un tipo de error muy frecuente , que podemos analizar , fjese en el EJEMPLO siguiente , ve algun problema ? . ( CON ERRORES ) #include <stdio.h> char *p , palabra[20] ;
____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 32

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

printf("Escriba su nombre : ") ; scanf("%s" , p ) ; palabra = " Como le va " ; printf("%s%s" , palabra , p ) ; } Pues hay dos errores , a falta de uno , el primero ya fue analizado antes , la expresin scanf("%s" , p ) es correcta pero , el error implcito es no haber inicializado al puntero p , el cual slo fu definido , pero aun no apunta a ningun lado vlido . El segundo error est dado por la expresin : palabra = " Como le va " ; ( tambin visto anteriormente ) ya que el nombre del array es una constante y no puede ser asignado a otro valor . Como lo escribiramos para que funcione correctamente ?

(CORRECTO) #include <stdio.h> #include <stdlib.h> #include <string.h> char *p , palabra[20] ; p = (char *)malloc(sizeof(char)128) ; printf("Escriba su nombre : ") ; scanf("%s" , p ) ; strcpy(palabra , " Como le va " ) ; printf("%s%s" , palabra , p ) ; } Observe que antes de scanf() se ha inicializado a p, mediante el retorno de malloc() y a al array palabra se le copiado el string mediante la funcin vista anteriormente strcpy(). Debemos aclarar tambin que, la secuencia de control %s en el printf() impone enviar a la pantalla un string, estando ste apuntado por el argumento siguiente al control, ste puede ser tanto el nombre de un array, como un puntero, ya que ambos explicitan direcciones. Una forma alternativa de resolverlo , sera: ( CORRECTO )

____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 33

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

#include <stdio.h> main() { char p[20] , *palabra ; printf("Escriba su nombre : ") ; scanf("%s" , p ) ; palabra = " Como le va " ; printf("%s%s" , palabra , p ) ; }

Obsrvese , que es idntico al primero , con la salvedad que se ha invertido las declaraciones de las variables , ahora el puntero es palabra y el array es p . Ambas soluciones son equivalentes y depender del resto del programa , cual es la mejor eleccin . ARRAYS DE PUNTEROS Es una prctica muy habitual , sobre todo cuando se tiene que tratar con strings de distinta longitud , generar array cuyos elementos son punteros , que albergarn las direcciones de dichos strings. Si imaginamos a un puntero como una flecha , un array de ellos equivaldra a un carcaj indio lleno de aquellas . Asi como: char *flecha; defina a un puntero a un caracter , la definicin char *carcaj[5]; implica un array de 5 punteros a caracteres . INICIALIZACION DE ARRAYS DE PUNTEROS Los arrays de punteros pueden ser inicializados de la misma forma que un array comn , es decir dando los valores de sus elementos , durante su definicin , por ejemplo si quisieramos tener un array donde el subndice de los elementos coincidiera con el nombre de los das de la semana , podramos escribir : char *dias[] = { "nmero de da no vlido" , "lunes" , "martes" , "miercoles" , "jueves" , "viernes" , "sabado" , "por fn es domingo"
____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 34

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

} Igual que antes, no es necesario en este caso indicar la cantidad de elementos , ya que el compilador los calcula por la cantidad de trminos dados en la inicializacin. Asi el elemento dias[0] ser un puntero con la direccin del primer string, dias[1], la del segundo, etc. PUNTEROS A ESTRUCTURAS Los punteros pueden tambin servir para el manejo de estructuras , y su alojamiento dinmico , pero tienen adems la propiedad de poder direccionar a los miembros de las mismas utilizando un operador particular , el -> , (escrito con los smbolos "menos" seguido por "mayor" ) . Supongamos crear una estructura y luego asignar valores a sus miembros , por los mtodos ya descriptos anteriormente : struct conjunto { int a ; double b ; char c[5] ; } stconj ; stconj.a = 10 ;

stconj.b = 1.15 ; stconj.c[0] = 'A' ; La forma de realizar lo mismo , mediante el uso de un puntero, sera la siguiente : struct conjunto { int a ; double b ; char c[5] ; } *ptrconj ; ptrconj = (struct conjunto *)malloc( sizeof( struct conjunto )) ; ptrconj->a = 10 ;

ptrconj->b = 1.15 ; ptrconj->c[0] = 'A' ; En este caso vemos que antes de inicializar un elemento de la estructura es necesario alojarla en la memoria mediante malloc(), observe atentamente la instruccin: primero se indica que el puntero que devuelve la funcin sea del tipo de apuntador a conjunto (sto es slo formal), y luego con sizeof se le da como argumento las dimensiones en bytes de la estructura. Ac se puede notar la ventaja del uso del typedef , para ahorrar tediosas repeticiones de texto, y mejorar la legilibilidad de los listados; podramos escribir: typedef struct { int a ;

____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 35

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

double b ; char c[5] ; } conj ; conj *ptrconj ; ptrconj = ( conj *)malloc( sizeof( conj )) ; Es muy importante ac , repasar la TABLA 13 del final del captulo 3 , donde se indican las precedencias de los operadores , a fn de evitar comportamientos no deseados , cuando se usan simultaneamente varios de ellos . Ya que c es un array podemos escribir : x = *ptrconj -> c ; la duda ac es, si nos referimos al contenido apuntado por ptrconj por c. Vemos en la tabla que, el operador -> es de mayor precedencia que la de * (dereferenciacin), por lo que, el resultado de la expresin es asignar el valor apuntado por c, es decir el contenido de c[0] . De la misma forma: *ptrconj -> c++ ; ++ptrconj -> c ; incrementa el puntero c , haciendolo tener la direccion de c[1] y luego extrae el valor de ste . incrementa el valor de c[0] .

En caso de duda , es conveniente el uso a discrecin de parntesis , para saltar por sobre las , a veces complicadas , reglas que impone la precedencia as , si queremos por ejemplo el valor de c[3] , la forma ms clara de escribir es: *( ptrconj -> ( c + 4 ) ) ; (Recuerde que c[3] es el CUARTO elemento del array ). PUNTEROS Y FUNCIONES La relacin entre los punteros y las funciones , puede verse en tres casos distintos , podemos pasarle a una funcin un puntero como argumento (por supuesto si su parmetro es un puntero del mismo tipo ) , pueden devolver un puntero de cualquier tipo , como ya hemos visto con malloc() y calloc() , y es posible tambin apuntar a la direccin de la funcin , en otras palabras , al cdigo en vez de a un dato. PUNTEROS COMO PARAMETROS DE FUNCIONES . Supongamos que hemos declarado una estructura , se puede pasar a una funcin como argumento , de la manera que ya vimos anteriormente: struct conjunto { int a ; double b ; char c[5] ; } datos ; void una_funcion( struct conjunto datos );

____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 36

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

Hicimos notar, en su momento, que en este caso la estructura se copiaba en el stack y as era pasada a la funcin, con el peligro que esto implicaba, si ella era muy masiva, de agotarlo. Otra forma equivalente es utilizar un puntero a la estructura : struct conjunto { int a ;

double b ; char c[5] ; } *pdatos ;

void una_funcion( struct conjunto *pdatos ) ; Con lo que slo ocupo lugar en el stack para pasarle la direccin de la misma. Luego en la funcin, como todos los miembros de la estructuras son accesibles por medio del puntero, tengo pleno control de la misma. Un ejemplo de funciones ya usadas que poseen como parmetros a punteros son: scanf(puntero_a_string_de_control, punteros_a_variables) printf(puntero_a_string_de_control, variables) En ambas vemos que los strings de control son , como no podra ser de otro modo , punteros , es decir que los podramos definir fuera de la funcin y luego pasarselos a ellas : p_control = "valor : %d " ; printf( p_control , var ) ; PUNTEROS COMO RESULTADO DE UNA FUNCION Las funciones que retornan punteros son por lo general aquellas que modifican un argumento , que les ha sido pasado por direccin ( por medio de un puntero ) , devolviendo un puntero a dicho argumento modificado , las que reservan lugar en el Heap para las variables dinmicas , retornando un puntero a dicho bloque de memoria . As podremos declarar funcines del tipo de: char *funcion1( char * var1 ) ; double *funcion2(int i , double j , char *k ) ; struct item *funcion3( struct stock *puntst ) ; El retorno de las mismas puede inicializar punteros del mismo tipo al devuelto , distinto , por medio del uso del casting . Algunas funciones , tales como malloc() y calloc() definen su retorno como punteros a void : void *malloc( int tamano ) ; de esta forma al invocarlas , debemos indicar el tipo de puntero de deseamos p = (double *)malloc( 64 ) ;

____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 37

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

OPERADORES DE PUNTEROS. En esta parte veremos dos operadores de punteros * y & los cuales se conocen respectivamente como operador de indireccin o desreferencia y el segundo como operador de direccin la manera de emplearse cada uno de los operadores se muestra en el siguiente programa. #include <stdio.h> #include <conio.h> void main() { int *xptr,x=10,z; clrscr(); xptr=&x; z=*xptr; printf(la direccin de x es %p, el contenido de la direccin apuntada por xptr es %d ,xptr,z); getch(); } Del programa anterior podemos observar que usamos al operador & para extraer la direccin de x y asignrselo al puntero, y si al puntero se le aplica el operador * se obtiene el contenido de la direccin apuntada por el puntero, en la lnea en la que se despliega el resultado vemos que el cdigo de formato para un puntero es %p. OPERACIONES ARITMTICAS CON PUNTEROS.- Los punteros solo pueden efectuar dos operaciones aritmticas la suma y la resta, las cantidades que se le pueden sumar o restar a los punteros son nmeros enteros, el resultado de sumar o de restar a un puntero ser un numero hexadecimal el cual depender del tipo de puntero es decir que si el puntero es de tipo entero al sumarle un uno, su direccin se modificara en dos unidades hexadecimales pues el tipo entero tiene como tamao dos bytes por lo que cada vez que se le sume un entero se incrementara la direccin en dos, en el tipo de carcter ser de uno en uno porque el tipo carcter solo requiere de un byte, tambin cambiara para el tipo real pues ser de 4 bytes para float y de 5 para el tipo double, la manera en la que se efectan las operaciones son mediante los operadores -,+,+=,=,++,-- por medio de las cuales se le agregan nmeros enteros a los punteros, las operaciones con puntero tienen sentido cuando se utilizan con arreglos pues las direcciones de memoria son continuas y tiene sentido moverse en direcciones de memoria que representan las distintas localidades del arreglo y pueden usarse dos o mas punteros y hacer operaciones entre ellos siempre y cuando estn relacionados por el arreglo, a continuacin se dan ejemplos de operaciones con punteros. int *xptr,x; Suponiendo que la direccin de x es 0001 y se le asigne al puntero si le suma 1 la nueva direccin como se muestra en la siguiente tabla. PUNTERO xptr xptr+1 xptr+2 DIRECCIN 001 002 004

PUNTEROS Y ARREGLOS.- Existe una relacin muy estrecha entre los punteros y los arreglos de hecho se define que el arreglo es en si un apuntador constante, si a un arreglo se le aplica un apuntador este apuntara a la direccin inicial del arreglo y si el puntero se le suma o resta se mover sobre el arreglo como se hace con las localidades del arreglos

____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 38

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

int x[20]; xptr=x; o xptr=&x[0] OPERADOR sizeof().- Este operador es muy til cuando se manejan punteros pues nos ayuda a definir el tamao en bytes de los tipos de datos estndar, este operador devuelve un valor entero el cual nos dice el tamao del dato por lo que esta funcin se deber asignar a una variable de tipo entero o ejecutarse dentro de una sentencia printf() como se muestra en el ejemplo. int y; y=sizeof(int); #include <stdio.h> #include <conio.h> void main() { printf(sizeof(char)= %d\n sizeof(short)= %d\n sizeof(int)= %d\n sizeof(long)= %d\n sizeof(float)= %d\n sizeof(double)= %d\n sizeof(long double)= %d\n ,sizeof(char),sizeof(short) ,sizeof(int),sizeof(long),sizeof(float),sizeof(double),sizeof(long double)); getch(); } ARREGLOS DE APUNTADORES.- Los arreglos pueden contener apuntadores y una manera comn para esta estructura de datos es formar un arreglo de cadenas, al cual se le conoce como arreglo de cadenas, en donde cada una de las localidades es una cadena, pero en el turbo C a las cadenas es esencial que tengan un apuntador a su primer carcter, a continuacin se muestra. char *cartas[4]={Corazones,Diamante,Picas,Treboles} La declaracin anterior nos muestra que lo que estamos declarando es un arreglo de apuntadores y aunque lo estamos igualando a cuatro cadenas, en realidad solo se esta almacenando sus apuntadores.

2.3.3. LONGITUD, SUBCADENA, CONCATENACION CON ARREGLOS DE CARACTERES 2.3.4. IMPLEMENTACION DE ALGORITMOS DE ORDENACIN Y BSQUEDA SECUENCIAL EN REGISTROS Ordenacin mtodo de la burbuja. Sea A una lista de n nmeros la ordenacin es aquella operacin que consiste en reorganizar los elementos de A para colocarlos en un orden creciente:
____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 39

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

A[1] < A[2] < A[3] < ... < A[n] Por ejemplo supongamos que A es la lista siguiente: 8, 4, 19, 2, 7, 13, 5, 16. Despus de la ordenacin quedara de la siguiente manera: 2, 4, 5, 7, 8, 13, 16, 19. Aunque la ordenacin para A, a simple vista es una tarea trivial, ordenar eficientemente es una tarea complicada. De hacho existe una gran variedad de algoritmos de ordenacin. Un algoritmo muy simple conocido como el mtodo de la burbuja es la comn ms mente usada. La definicin de ordenacin dada se refiere nicamente a la ordenacin de datos numricos en orden creciente esta restriccin es solo por conveniencia de notacin, la ordenacin puede abarcar tambin la colocacin de datos numricos en orden decreciente o de datos no numricos en ordenacin alfabtica, ordenar A implica colocar los registros de A en un determinado orden impuesto pero un campo de clave de los mismos.

METODO DE LA BURBUJA.
Supongamos que almacenar en memoria la lista de nmeros A[1], A[2], ..., A[n]. El algoritmo del mtodo de la burbuja funciona de la siguiente manera: 1. Comparamos A[1] y A[2] y colocamos ambos en el orden deseado de tal forma que A[1]<A[2] despus de la operacin. Despus comparamos A[2] con A[3] y colocamos ambos en tal horma que A[2]<A[3] y a continuacin comparamos A[3] y A[4] y colocamos de tal forma que A[3]<A[4], a continuacin de la siguiente forma comparamos A[n-1] y A[n] y lo colocamos en el orden adecuado. Observe que le paso 1 implica n-1 comparaciones (durante el paso 1 el elemento mayor burbujea hacia arriba hasta llegar a la posicin n-esima) cuando termina el paso A[n] almacenada el elemento mayor. 2. Repetir el paso 1 pero con una comparacin menos en decir para remos despus de estudiar la posible reordenacin de A[n-2] y de A[n-1]. Cuando completamos este segundo paso el segundo elemento mayor ocupara el lugar A[n-1]. 3. Repetir el paso 1 con dos comparaciones menos, es decir parando despus de estudiar A[n-3] y A[n2]... Paso n-1. Comparemos A[1] con A[2] y lo colocamos en el orden adecuado. Despus de n-1 pasos la lista quedaran ordenada en orden creciente. El proceso de atravesar totalmente o parcialmente una lista recibe el nombre de recorrido, por tanto cada uno de los pasos anteriores constituye un recorrido. De acuerdo con esto, el algoritmo de ordenacin por la burbuja necesita n-1 recorridos donde n es el nmero de elementos de la entrada. Supongamos que en el arreglo A almacena los siguientes nmeros: A=[32, 51, 27, 57, 66, 23, 13, 57].

____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 40

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

PASO I Comparamos A1 y A2, puesto que 32<51 no altera la lista. Comparamos A2 y A3 puesto que 31>27 intercambiamos ambos y la lista quedara: 32, 27, 51, 87, 66, 23, 13, 57. Comparamos A3 y A4 puesto que 51<87 no alteramos la lista. Comparamos A4y A5 puesto que 87>66 intercambiamos ambos y la lista quedara: 32, 27, 51, 66, 87, 23, 13, 57. Comparamos A5 y A6 puesto que 87>23 intercambiamos ambos y la lista quedara: 32, 27, 51, 66, 23, 87, 13, 57. Comparamos A6 y A7 puesto que 87>13 intercambiamos ambos y la lista quedara: 32, 27, 51, 66, 23, 13, 87, 57. Comparamos A7 y A8 puesto que 87>57 intercambiamos ambos la lista quedara: 32, 27, 51, 66, 23, 13, 57, 87. Al final del paso I el nmero 87 es el mayor de todos ocupa la ultima posicin sin embargo el resto de los nmeros no estn ordenados, aunque algunos de ellos han cambiado de posicin. Para los pasos restantes indicamos solo los intercambios. PASO II 32, 27, 51, 66, 23, 13, 57, 87. 27, 32, 51, 66, 23, 13, 57, 87. 27, 32, 51, 23, 66, 13, 57, 87. 27, 32, 51, 23, 13, 66, 57, 87. 27, 32, 51, 23, 13, 57, 66, 87. Al final de paso II, el siguiente nmero ms alto aparece en la penltima posicin.

PASO III 27, 32, 23, 51, 13, 57, 66, 87. 27, 32, 23, 13, 51, 57, 66, 87. PASO IV 27, 23, 32, 13, 51, 57, 66, 87. 27, 23, 13, 32, 51, 57, 66, 87. PASO V 23, 27, 13, 32, 51, 57, 66, 87. 23, 13, 27, 32, 51, 57, 66, 87. PASO VI 13, 23, 27, 32, 51, 57, 66, 87 El paso VI implica dos comparaciones A1 y de A2 con A3. La segunda comparacin no implica intercambio.
____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 41

ESTRUCTURA DE DATOS INCORPORADOS UNIDAD II ____________________________________________________________________________________________________

El paso VII finalmente comparamos A1 con A2, puesto que 13 es menor que 23 no realiza ningn intercambio.

BUSQUEDA SECUENCIAL.
Sea DATOS un conjunto de datos almacenados en memoria. Supongamos que se nos da un determinado valor de tipo almacenado, ELEMENTO. l termino BUSQUEDA hace referencia a la operacin consistente en encontrar la posicin que ocupa ELEMENTO en DATOS o bien a la impresin de un mensaje que nos indica que ELEMENTO no se encuentra en la lista. Decimos ELEMENTO en DATOS. En caso contrario diremos que la bsqueda ha sido fallida. Frecuentemente queremos insertar ELEMENTO en DATOS si la bsqueda ha sido fallida. En este caso podemos utilizar algoritmos de bsqueda siempre. Existen varios algoritmos de bsqueda distintos. El algoritmo elegido depende de la forma en que se encuentra organizados los datos. Sea DATOS un arreglo lineal con n ELEMENTOS. Si no disponemos de ms informacin acerca de DATOS la forma ms rpida de localizar el ELEMENTO dentro de DATOS de uno en uno as comparamos si DATOS{1}=ELEMENTO, en caso contrario DATOS{2}=ELEMENTO y as sucesivamente este mtodo que recorre DATOS, secuencialmente hasta localizar ELEMENTO recibe el nombre de Bsqueda secuencial o Bsqueda lineal.

____________________________________________________________________________________________________ ESTRUCTURA DE DATOS ING. OSORNIO 42