TEMA 6: PUNTEROS 1 PUNTEROS......................................................2 2 LOS OPERADORES DE PUNTEROS ............2 3 ASIGNACIÓN Y COMPARACIÓN DE PUNTEROS ..............................................................3 4 INICIALIZACIÓN DE PUNTEROS...................4 5 PROBLEMAS CON LOS PUNTEROS ............5 5.1 EJEMPLO DE PUNTERO QUE NO APUNTA AL TIPO CORRECTO.......................................................................................................................... 5 5.2 EJEMPLO DE PUNTERO NO INICIALIZADO .................................. 6 5.3 EJEMPLO DE PUNTERO USADO DE FORMA INCORRECTA . 6 Tema 6 Pág. 1 1 Punteros Una variable puntero es una variable estática como cualquier otra que, en vez de contener valores de datos, contiene valores que representan direcciones de memoria. Estas direcciones generalmente, representa la dirección de memoria donde se encuentra otra variable. Cuando el puntero contiene la dirección de otra variable decimos que el puntero apunta a dicha variable. Para que pueda acceder correctamente a la variable a la que apunta, el puntero debe conocer que tipo de datos almacena. Así pues, el tipo del puntero debe coincidir con el de los datos que apunta. tipo *nombre puntero; Por ejemplo: int *p; El asterisco le indica al compilador que p no va a guardar un dato de tipo int, sino una dirección para apuntar a una variable int. 2 Los operadores de punteros Existen dos operadores especiales que se usan con los punteros: * y &. El operador & (operador dirección), devuelve la dirección de memoria de la variable a la que afecte. Así, por ejemplo p = &x; El operador * (operador indirección u operador contenido) aplicado sobre una dirección de memoria, es decir, sobre una variable de tipo puntero, permite acceder al valor de la variable situada en esa dirección de memoria. Nombre de la Dirección de memoria Variable en variable memoria X 1002 1250 1004 1008 P 1010 1002 *p 1012 Tema 6 Pág. 2 Ejemplo: #include <stdio.h> int main() { int cuenta, q; int *m; cuenta = 100;/*a cuenta se le asigna 100 */ m = &cuenta;/*m =recibe direc. de cuenta*/ q = *m;/*q=cuenta a través de m */ printf(“%i”,q);/*muestra en pantalla 100 */ return 0; } 3 Asignación y comparación de punteros En general, los punteros se ajustan a las mismas reglas que cualquier otra variable y expresión en C, pudiendo utilizarse a la derecha de una declaración de asignación para asignar su valor a otro puntero. De la misma forma, podemos comparar dos punteros en una expresión relacional. #include <stdio.h> int main() { int i=100; int *p1, *p2; p1 = &i; p2 = p1; if (p1==p2) /*comparación de punteros */ printf(“p1 apunta a la misma dirección que p2”); printf (“El valor de *p2 es %d\n”, *p2); i--; prinft(“El valor de i es %d\n”, i); } Tema 6 Pág. 3 4 Inicialización de punteros Al igual que otras variables, C no inicializa los punteros cuando se declaran y es preciso inicializarlos antes de su uso. Si se trata de usar el puntero antes de asignar un valor (una dirección), como la dirección a la que apunta es desconocida, al usarla puede que estemos modificando una parte de la memoria reservada al sistema operativo o al código máquina de nuestro programa o de algún otro. Nunca se debe manipular la zona de memoria apuntada por un puntero si no tenemos la seguridad de que dicha zona de memoria está correctamente reservada. Debemos por tanto inicializar el puntero antes de utilizarlo o hacer que explícitamente un puntero no apunte a ningún sitio. Por convención se da a un puntero que apunta a ningún sitio el valor nulo para significar que el puntero apunta a nada. Así pues, para un puntero existen tres posibilidades: 1. Estar indefinido (lo más peligroso) 2. No apuntar a nada 3. Apuntar a una variable o dirección de memoria determinada. Hacer que un puntero no apunte a ningún sitio implica asignarle un valor especial predefinido llamado NULL: int * p; P=NULL; Se puede decir ahora que p no apunta a nada. Es una buena idea asignar el valor NULL a todos los punteros tan pronto como el programa empieza su ejecución. Utilizar un puntero con un valor NULL es más seguro que utilizar un puntero indefinido. Sin embargo, llegar a ser un puntero con un valor nulo no hace más seguro su uso. Si se usa un puntero nulo en el lado izquierdo de una sentencia de asignación, se correría todavía el riesgo de estropear el programa o el sistema operativo. Tema 6 Pág. 4 5 Problemas con los punteros Desgraciadamente, el uso de punteros es fuente continua de errores, tanto al compilar como, lo que es peor, a la hora de ejecutar. donde tendremos muchas mas dificultades para localizar los fallos. Los punteros ofrecen mucho poder y son necesarios para muchos programas, pero nada dará más problemas que un puntero incontrolado. Si un puntero contiene accidentalmente un valor erróneo, puede ser el fallo más difícil de depurar, ya que en tiempo de compilación no se detecta nada y durante la ejecución del programa pueden producirse efectos colaterales que son difíciles de detectar. 5.1 Ejemplo de puntero que no apunta al tipo correcto #include <stdio.h> int main() { float x = 55.4; int *p; /*p es un puntero a un entero*/ p = &x; /*ERROR! Hacemos que el puntero a entero p apunte a una variable float*/ printf("El valor correcto es: %f\n", x); printf("Valor apuntado (como float): %f\n", *p); printf("Valor apuntado (como int) : %d\n", *p); return 0; } El resultado por pantalla es algo asi: El valor correcto es: 55.400000 Valor apuntado (como float): 508524232706.400024 Valor apuntado (como int) : -26214 El compilador no produce errores, y completa su tarea. Como mucho nos muestra un warning, avisando de la operación sospechosa ("Suspicious pointer conversion in function main" (conversión de puntero sospechosa en la función main)). Tema 6 Pág. 5 5.2 Ejemplo de puntero no inicializado int main() { int i, *p; i = 50; *p = i; /* ¡ERROR! Usamos el puntero p sin inicializarlo. ¿A dónde apunta? */ printf(“El valor de i es %d\n”, i); printf(“El valor de *p es %d\n”, *p); return 0; } Este programa esta mal porque asigna el valor de i (50) a alguna dirección de memoria desconocida. Para evitar este tipo de problemas debemos siempre asegurarnos que un puntero apunta a algo válido antes de usarlo. La compilación no producen errores y como mucho, dependiendo del compilador, se producirá un warning, avisando de la operación sospechosa ("Possible use of pointer before definition" (Posible uso de un puntero antes de definirlo)). 5.3 Ejemplo de puntero usado de forma incorrecta Este error suele ocurrir cuando olvidamos poner el operador de dirección (&). P.ej: #include <stdio.h> int main() { int i, *p; i = 50; p = i; /* ERROR, p=al valor de i en vez de su dirección */ printf(“El valor de i es %d\n”, i); printf(“El valor de *p es %d\n”, *p); return 0; } La última llamada a printf no imprimirá el valor de i sino un valor desconocido. Al compilar, puede producir (depende del compilador) un warning: "Suspicious pointer conversion" o "Nonportable pointer conversion", dependiendo de los casos. Tema 6 Pág. 6