Facultad de Informática, Electrónica y Comunicación Estructuras de datos -Inf200 e Inf212 Apuntadores Punteros o apuntadores o pointers Un puntero es una variable que contiene una dirección de memoria. Normalmente, esa dirección es la posición de otra variable en la memoria. Si la variable p de tipo float, está en la dirección 1111 y la variable ptr en la dirección 1021, entonces, si el contenido de ptr es la dirección de p, es decir 1111, decimos que ptr es un apuntador a p. ptr
p
1021
1111
1111 45.23
Algunas consideraciones para utilizar punteros o apuntadores: Existen dos operadores especiales de punteros: & y *. El operador de dirección (&) devuelve la dirección de memoria de su operando. El operador de indirección (*) devuelve el contenido de la dirección apuntada por el operando. Los punteros apuntan a un tipo de variable, el cual se define en la declaración, por ejemplo; int *ptr1; /* apunta solo a variables tipo int */ float *ptr; /* apunta solo a variables tipo float */ char *prt2; /* apunta solo a variable tipo char */ El operador de dirección & delante del nombre de una variable nos devuelve la dirección de la variable, ejemplo; int a; ptr1 = &a; float p, q; ptr = &p; El valor apuntado por un puntero se obtiene con el operador de indirección *. Si usamos la ilustración anterior *ptr corresponde al valor 45.23 de la variable p. Si hacemos q = *ptr; entonces q y p tienen el mismo valor, pero ptr no apunta a q, sino a p. En conclusión, si ptr contiene la dirección de memoria de la variable p, entonces ptr apunta a p y *ptr indica el contenido de p, es decir, cuando se modifica o cambia el valor de *ptr se cambia también el valor de la variable p. Concretamente si hacemos *ptr = 100; entonces, el valor de la variable p también es 100. Todo esto se da porque ptr a punta a p o de manera equivalente, porque ptr contiene la dirección de p. De manera general una declaración de un puntero consiste en un tipo base(int, char, flotat), un asterisco * y el nombre de la variable. La forma general es: tipo *nombre_de _variable; Se pueden declarar apuntadores a una estructura: typedef struct { int edad; float salario; /* declaración de tipo estructura global */ char nom[15]; }xxx; xxx
*ptr3;
Diego Santimateo G.
/* apuntador al tipo de estructura xxx */
1
Facultad de Informática, Electrónica y Comunicación Estructuras de datos -Inf200 e Inf212 Apuntadores Asignación de punteros Como en el caso de cualquier otra variable, un puntero puede utilizarse a la derecha de una declaración de asignación para asignar su valor a otro puntero. Por ejemplo: int x; int *p1,*p2; p1=&x; p2=p1; Tanto p1 como p2 apuntan a x.
Operaciones con punteros Existen sólo dos operaciones aritméticas que se pueden usar con punteros: la suma y la resta. Cada vez que se incrementa en una unidad un puntero, apunta a la posición de memoria del siguiente elemento de su tipo base(int, flota, char). Cada vez que se disminuye en una unidad, apunta a la posición del elemento anterior. Con punteros a variables del tipo char(un byte) parece una aritmética normal, sin embargo, el resto de los punteros aumentan o decrecen dependiendo de la longitud del tipo de datos a los que apuntan, piense en el tamaño de un registro. Por ejemplo, si asumimos que los enteros son de dos bytes de longitud y p1 es un puntero a entero con valor actual 2000. Entonces, después de la expresión p1++; p1 contiene el valor 2002, no 2001. Las variables punteros pueden ser comparadas siempre que éstas apunten a un mismo tipo de datos, por ejemplo; Int *ptr1, *ptr2; Podemos usar las siguientes expresiones lógicas: ptr1 < ptr2 ptr2 >= ptr1 ptr1 == ptr2 ptr2 != ptr1 ptr == NULL
Punteros y arrays Existe una estrecha relación entre los punteros y los arreglos. Recuerde que el nombre de un arreglo se considera puntero a la primera posición. char linea[80], *prt2; prt2 = linea; Aquí, prt2 ha sido asignado a la dirección del primer elemento del vector linea. Para acceder al quinto elemento de linea se escribe linea[4] o *(prt2 + 4). Los punteros pueden almacenarse en arreglos como cualquier otro tipo de datos. Por ejemplo, para un arreglo de 10 punteros a tipo entero, la declaración es: int *x[10]; Para asignar la dirección de una variable entera llamada var al tercer elemento de x se escribe: x[2] = &var; El valor de la variable var corresponde a *x[2];(valor que es apuntado por x[2]).
Indirección múltiple Se puede hacer que un puntero apunte a otro puntero que apunte a un valor de destino. Esta situación se denomina indirección múltiple o punteros a punteros. Una variable que es puntero a puntero tiene que declararse como tal. Esto se hace colocando un asterisco (*) adicional en frente del nombre de la variable. Por ejemplo, la siguiente declaración indica al compilador que ptr es un puntero a puntero de tipo float: float **ptr;
Funciones de asignación dinámica, malloc() y free() Hasta el momento hemos trabajado con estructuras de datos estáticas, no obstante los punteros proporcionan el soporte necesario para manejar estructuras de datos dinámica en C. La asignación dinámica es la forma en la que un programa puede obtener memoria mientras se está ejecutando y también puede liberarla cuando sea necesario.
Diego Santimateo G.
2
Facultad de Informática, Electrónica y Comunicación Estructuras de datos -Inf200 e Inf212 Apuntadores El centro del sistema de asignación dinámica está compuesto por las funciones (existentes en la biblioteca stdlib.h) malloc(), que asigna memoria y devuelve un puntero a un char. free() que la devuelve o la libera. Podemos invocar a la función malloc() así: ptr =malloc( n * sizeof(tipo de dato)); Tras una llamada fructífera, malloc() devuelve un puntero a un caracter, de manera que en el ejemplo, la declaración de ptr es char *ptr. Si no hay suficiente memoria libre para satisfacer la petición de malloc(), se devuelve el apuntador nulo. En el siguiente código se asigna en tiempo de ejecución 10 posiciones enteras (un vector de 10 posiciones) int *ptr2; ptr2 = (int *) malloc(50 * sizeof(int)); Nótese que se ha hecho una conversión del tipo char que devuelve malloc, a tipo int de ptr2. Después de la asignación, ptr2 apunta a la primera posición entera de las 50 solicitadas. La función free() es la opuesta de malloc() porque devuelve al sistema la memoria previamente asignada. Una vez que la memoria ha sido liberada, puede ser reutilizada en una posterior llamada a malloc(). El prototipo de la función free() es: void free (void *p); free(p);
Listas dinámicas Si se desea crear una lista dinámica cuyos nodos contengan diferentes tipos de datos, entonces, es necesario definir una estructura o registro que contenga un apuntador para ir enlazando los nodos, tal como se muestra en el diagrama:
inf. sig
inf.
sig
inf. sig
inf
sig
Inicio Null
Nodo1
Nodo2
Nodo3
Nodo4
La siguiente declaración se ajusta al diagrama: struct elementonodo { int inf; struct elementonodo *sig; }; typedef struct elementonodo nodo;
/* nodo es un tipo de estructura */
nodo
/ declara apuntadores al tipo nodo */
*inicio, *ptr;
Diego Santimateo G.
3
Facultad de Informática, Electrónica y Comunicación Estructuras de datos -Inf200 e Inf212 Apuntadores Para crear cada nodo se usa: ptr = (nodo * ) malloc (sizeof(nodo)); /* reserva espacio para un nodo */ La primera vez inicio = ptr; Podemos acceder a un campo de la estructura así: ptr -> inf = 100; ptr -> sig = NULL; ¿Cómo hacer para ir añadiendo nodos a la lista? ... escriba el código necesario.
Diego Santimateo G.
4