Está en la página 1de 9

Apuntadores en C y C++

Universidad de Carabobo Facultad Experimental de Ciencias y Tecnolog a Prof. Marcos A. Gil T. 8 de diciembre de 2004

1.

Introduccin o

Los apuntadores en C y C++ son una herramienta muy potente de programacin que suele causar mucha o confusin en los estudiantes que la estn aprendiendo. Adems, cuando los programadores cometen un error o a a en su utilizacin, puede ser muy dif encontrar el error, por lo cual es importante saber utilizarlos muy o cil bien. El uso de apuntadores en C y C++ es muy importante debido a que permite hacer los programas ms a ecientes y ms exibles. En en este art a culo se explica de una manera sencilla y breve todo lo referente a la utilizacin de apuntadores tanto en C como en C++. o Todo lo explicado en este art culo aplica tanto para C como para C++, a menos que se especique un lenguaje en particular. En algunos ejemplos de cdigo que son aplicables a C aparecen instrucciones de o entrada y salida de las librer estndar de C++. as a

2.

Denicin de apuntador o

Cuando se declara una variable, el compilador reserva un espacio de memoria para ella y asocia el nombre de sta a la direccin de memoria desde donde comienzan los datos de esa variable. Las direcciones de memoria e o se suelen describir como nmeros en hexadecimal. u Un apuntador es una variable cuyo valor es la direccin de memoria de otra variable. Se dice que un o apuntador apunta a la variable cuyo valor se almacena a partir de la direccin de memoria que contiene el o apuntador. Por ejemplo, si un apuntador p almacena la direccin de una variable x, se dice que p apunta a o x .

3.

Referenciacin o

La referenciacin es la obtencin de la direccin de una variable. En C y C++ esto se hace a travs del o o o e operador &, aplicado a la variable a la cual se desea saber su direccin. Ntese que se trata de un operador o o unario. Ejemplo: Cdigo C y C++ o int x = 25; cout << "La direccin de x es: " << &x << endl; o

Este cdigo imprime un valor del estilo 0x4fffd34. Este valor puede variar durante cada ejecucin del o o programa, debido a que el programa puede reservar distintos espacios de memoria durante cada ejecucin. o

4.

Declaracin de apuntadores o

Para declarar un apuntador se especica el tipo de dato al que apunta, el operador *, y el nombre del apuntador. La sintaxis es la siguiente: <tipo de dato apuntado> *<indenticador del apuntador> A continuacin se muestran varios ejemplos: o Cdigo C y C++ o int *ptr1; // Apuntador a un dato de tipo entero (int) char *cad1, *cad2; // Dos apuntadores a datos de tipo carcter (char) a float *ptr2; // Apuntador a un dato de tipo punto-flotante (float)

5.

Asignacin de apuntadores o

Se pueden asignar a un apuntador direcciones de variables a travs del operador de referenciacin (&) o e o direcciones almacenadas en otros apuntadores. Ejemplos: Cdigo C y C++ o int i = 5; int *p, *q; p = &i; q = p; // Se le asigna a p la direccin de i o // Se le asigna a q la direccin almacenada en p (la misma de i) o

6.

Desreferenciacin de apuntadores o

La desreferenciacin es la obtencin del valor almacenado en el espacio de memoria donde apunta un o o apuntador. En C y C++ esto se hace a travs del operador *, aplicado al apuntador que contiene la e direccin del valor. Ntese que se trata de un operador unario. Ejemplos: o o Cdigo C y C++ o int x = 17, y; int *p; p = &x; cout << "El valor de x es: " << *p << endl; // Imprime 17 y = *p + 3; // A y se le asigna 20

C++ adems provee el operador binario ->, utilizado para obtener campos de un registro con un apuntaa dor al mismo de una manera ms fcil y legible. Muchos compiladores de C tambin soportan este operador. a a e Ejemplo:

Cdigo C++ o struct Data { char nombre[20]; int edad; }; Data d; Data *pd = &d; (*pd).edad = 23; // Acceso al campo edad utilizando el operador . pd->edad = 23; // Acceso al campo edad utilizando el operador ->

7.

Vericacin de tipos en apuntadores o

Al igual que el resto de las variables, los apuntadores se enlazan a tipos de datos espec cos (apuntadores a variables de cierto tipo), de manera que a un apuntador slo se le pueden asignar direcciones de variables o del tipo especicado en la declaracin del apuntador. Ejemplo: o Cdigo C y C++ o int *p1; float *p2; int x; p1 = &x; // Esto es vlido a p2 = &x; // Esto no es vlido (el compilador genera un error) a

8.

Direcciones invlidas y la direccin NULL a o

Normalmente, un apuntador inicializado adecuadamente apunta a alguna posicin espec o ca de la memoria. Sin embargo, algunas veces es posible que un apuntador no contenga una direccin vlida, en cuyo caso o a es incorrecto desreferenciarlo (obtener el valor al que apunta) porque el programa tendr un comportamiento a impredecible y probablemente errneo, aunque es posible que funcione bien. Un apuntador puede contener o una direccin invlida debido a dos razones: o a 1. Cuando un apuntador se declara, al igual que cualquier otra variable, el mismo posee un valor cualquiera que no se puede conocer con antelacin, hasta que se inicialice con algn valor (direccin). Ejemplo: o u o Cdigo C y C++ o float *p; cout << "El valor apuntado por p es: " << *p << endl; // Incorrecto *p = 3.5; // Incorrecto 2. Despus de que un apuntador ha sido inicializado, la direccin que posee puede dejar de ser vlida si e o a se libera la memoria reservada en esa direccin, ya sea porque la variable asociada termina su mbito o a o porque ese espacio de memoria fue reservado dinmicamente y luego se liber1 . Ejemplo: a o

1 La

asignacin dinmica de memoria se explica en la seccin 13 o a o

Cdigo C y C++ o int *p, y; void func() { int x = 40; p = &x; y = *p; // Correcto *p = 23; // Correcto } void main() { func(); y = *p; // Incorrecto *p = 25; // Incorrecto }

Si se intenta desreferenciar un apuntador que contiene una direccin invlida pueden ocurrir cosas como o a las siguientes: Se obtiene un valor incorrecto en una o ms variables debido a que no fue debidamente inicializada a la zona de memoria que se accede a travs de la direccin en cuestin. Esto puede ocasionar que el e o o programa genere resultados incorrectos. Si casualmente la direccin es la misma de otra variable utilizada en el programa, o est dentro del o a rango de direcciones de una zona de memoria utilizada, existe el riesgo de sobreescribir datos de otras variables. Existe la posibilidad de que la direccin est fuera de la zona de memoria utilizada para almacenar o e datos y ms bien est, por ejemplo, en la zona donde se almacenan las instrucciones del programa. Al a e intentar escribir en dicha zona, fcilmente puede ocurrir que el programa genere un error de ejecucin a o y el sistema operativo lo detenga, o que el programa no responda y deje al sistema operativo inestable. En muchos casos el sistema operativo detecta el acceso inadecuado a una direccin de memoria, en cuyo o caso detiene abruptamente el programa. Cuando no se desea que un apuntador apunte a algo, se le suele asignar el valor NULL, en cuyo caso se dice que el apuntador es nulo (no apunta a nada). NULL es una macro t picamente denida en archivos de cabecera como stdef.h y stdlib.h. Normalmente, en C++ se encuentra disponible sin incluir ningn archivo u de cabecera. NULL se suele denir en estas librer as as : Cdigo C y C++ o #define NULL 0

Un apuntador nulo se utiliza para proporcionar a un programa un medio de conocer cundo un apuntador a contiene una direccin vlida. Se suele utilizar un test condicional para saber si un apuntador es nulo o no o a lo es, y tomar las medidas necesarias. El valor NULL es muy util para la construccin de estructuras de o datos dinmicas, como las listas enlazadas, matrices esparcidas, etc. Es igualmente incorrecto desreferenciar a el valor NULL por las mismas razones presentadas previamente.

9.

Apuntadores a apuntadores

Dado que un apuntador es una variable que apunta a otra, fcilmente se puede deducir que pueden existir a apuntadores a apuntadores, y a su vez los segundos pueden apuntar a apuntadores, y as sucesivamente. Estos 4

apuntadores se declaran colocando tantos asteriscos (*) como sea necesario. Ejemplo: Cdigo C y C++ o char c = z; char *pc = &c; char **ppc = &pc; char ***pppc = &ppc; ***pppc = m; // Cambia el valor de c a m

10.

Apuntadores constantes y apuntadores a constantes

Es posible declarar apuntadores constantes. De esta manera, no se permite la modicacin de la direccin o o almacenada en el apuntador, pero s se permite la modicacin del valor al que apunta. Ejemplo: o Cdigo C y C++ o int x = 5, y = 7; int *const p = &x; // Declaracin e inicializacin del apuntador constante o o *p = 3; // Esto es vlido a p = &y; // Esto no es vlido (el compilador genera un error) a

Tambin es posible declarar apuntadores a datos constantes. Esto hace que no sea posible modicar el e valor al que apunta el apuntador. Ejemplo: Cdigo C y C++ o int x = 5, y = 7; const int *p = &x; // Declaracin e inicializacin del apuntador a constante o o p = &y; // Esto es vlido a *p = 3; // Esto no es vlido (el compilador genera un error) a y = 3; // Esto es vlido a

11.

Apuntadores, arreglos y aritmtica de apuntadores e

Los arreglos y apuntadores estn fuertemente relacionados. El nombre de un arreglo es simplemente a un apuntador constante al inicio del arreglo. Se pueden direccionar arreglos como si fueran apuntadores y apuntadores como si fueran arreglos. Ejemplos: Cdigo C y C++ o int lista_arr[5] = {10, 20, 30, 40, 50}; int *lista_ptr; lista_ptr = lista_arr; // A partir de aqu ambas variables apuntan al mismo sitio cout << lista_arr[0]; // Imprime 10 cout << lista_ptr[0]; // Instruccin equivalente a la anterior o cout << *lista_arr; // Instruccin equivalente a la anterior o cout << *lista_ptr; // Instruccin equivalente a la anterior o cout << lista_arr[3]; // Imprime 40 cout << lista_ptr[3]; // Instruccin equivalente a la anterior o

Es posible sumar y restar valores enteros a un apuntador. El resultado de estas operaciones es el desplazamiento de la direccin de memoria hacia adelante (suma) o hacia atrs (resta) por bloques de bytes del o a tamao del tipo de dato apuntado por el apuntador. Esto permite recorrer arreglos utilizando apuntadores. n Ejemplos: 5

Cdigo C y C++ o int lista[5] = {10, 20, 30, 40, 50}; int *p; char cad[15]; char *q; p = &lista[3]; p = lista + 3; cout << lista[2]; cout << *(lista+2); // // // // p almacena la direccin de la posicin 3 del arreglo o o Instruccin equivalente a la anterior o Imprime 30; Instruccin equivalente a la anterior o

// Las siguientes instrucciones imprimen la palabra "Programando" /* Nota: Recurdese que una constante de cadena de caracteres es e una secuencia de caracteres en memoria seguidos del cracter nulo */ a strcpy(cad, "Programando"); for (q = cad; *q != \0; q++) cout << q;

Tambin es posible restar dos apuntadores. El resultado de esta operacin es el nmero de bloques de e o u bytes que hay entre las dos direcciones del tamao del tipo de dato apuntado por los apuntadores. Ejemplo: n Cdigo C y C++ o double x[5] = {1.1, 2.1, 3.1, 4.1, 5.1}; double *p = &x[1], *q = &x[4]; int n; n = q - p; // a n se le asigna 3

12.

Apuntadores para paso de parmetros por referencia a

El lenguaje C no provee una manera de pasar parmetros por referencia. Sin embargo, es posible hacerlo a a travs del uso de apuntadores. A continuacin se muestra un ejemplo del paso de un parmetro por referencia e o a en C++, y luego un cdigo equivalente en C o C++ utilizando un apuntador: o Cdigo C++ o void suma(int a, int b, int& r) { r = a + b; } void main() { int x; suma(7, 5, x); cout << "7 + 5 = " << x; }

Cdigo C y C++ o void suma(int a, int b, int *r) { *r = a + b; } void main() { int x; suma(7, 5, &x); cout << "7 + 5 = " << x; }

Ntese que en ambos casos se utiliza el operador & para cosas distintas. El operador & tiene dos o signicados como operador unario: sealacin de parmetro por referencia y operador de referenciacin. n o a o

13.

Asignacin dinmica de memoria o a

Los programas pueden crear variables globales o locales. Las variables declaradas globales en sus programas se almacenan en posiciones jas de memoria, en la zona conocida como segmento de datos del programa, y todas las funciones pueden utilizar estas variables. Las variables locales se almacenan en la pila (stack ) y existen slo mientras estn activas las funciones donde estn declaradas. En ambos casos el espacio de o a a almacenamiento se reserva en el momento de la compilacin del programa. o Tambin es posible reservar y utilizar memoria dinmicamente, tomada de la zona de memoria llamada e a mont culo (heap) o almacn libre. En C estn disponibles varias funciones que permiten realizar reservar y e a librerar memoria, pero C++ adems provee un mtodo ms fcil y seguro de hacerlo. a e a a

13.1.

Asignacin dinmica de memoria al estilo de C o a

Para asignar memoria dinmicamente en C se utilizan las funciones malloc() y free(), denidas t a picamente en el archivo de cabecera stdlib.h. La funcin malloc() reserva memoria y retorna su direccin, o o o retorna NULL en caso de no haber conseguido suciente memoria; y la funcin free() permite liberar la o memoria reservada a travs de un apuntador. La sintaxis de ambas funciones es como sigue: e Cdigo C y C++ o void *malloc(size_t tam_bloque); // size_t es un tipo de datos entero. // tam_bloque es el tama~o en bytes n // del bloque de memoria a reservar. void free(void *bloque); // bloque es un apuntador a la zona // de memoria a liberar.

Como se puede apreciar, malloc() reserva memoria sin importar el tipo de datos que se almacenar en a ella, retornando un apuntador a void. Por esta razn, se hace necesario convertir el tipo de apuntador al o tipo del apuntador que guardar la direccin. Tambin es necesario calcular exactamente cuntos bytes se a o e a requieren reservar, ayudndose con el uso del operador sizeof. Ejemplos: a

Cdigo C y C++ o typedef struct { char nombre[20]; int edad; } Data; Data *p_data; // Declaracin de un apuntador a Data o int i; p_data = (Data*) malloc(sizeof(Data)); // Reservacin de memoria para un registro o if (p_data != NULL) { strcpy(p_data->nombre, "Rachel"); p_data->edad = 21; free(p_data); } // Reservacin de memoria para un arreglo de 10 registros o p_data = (Data*) malloc(sizeof(Data)*10); if (p_data != NULL) // Verificacin de reservacin o o { // Lectura de datos del arreglo for (i = 0; i < 10; i++) cin >> p_data[i].nombre >> p_data[i].edad; // Liberacin de memoria del arreglo o free(p_data); } // Verificacin de reservacin o o // Inicializacin de datos o // en la memoria reservada // Liberacin de memoria o

13.2.

Asignacin dinmica de memoria al estilo de C++ o a

Para asignar memoria dinmicamente en C++ se utilizan los operadores new y delete. El operador new a reserva memoria para un tipo de datos espec co y retorna su direccin, o retorna NULL en caso de no haber o conseguido suciente memoria; y el operador delete permite liberar la memoria reservada a travs de un e apuntador. La sintaxis de ambos operadores es como sigue: Para reservar y liberar un solo bloque: <apuntador> = new <tipo de dato> delete <apuntador> Para reservar y liberar varios bloques (un arreglo): <apuntador> = new <tipo de dato>[<nmero de bloques>] u delete [] <apuntador> El tipo del apuntador especicado del lado izquierdo del operador new debe coincidir con el tipo especicado del lado derecho. De no ser as se produce un error de compilacin. Ejemplos: , o

Cdigo C++ o struct Data { char nombre[20]; int edad; }; Data *p_data; // Declaracin de un apuntador a Data o int i; p_data = new Data; // Reservacin de memoria para un registro o if (p_data != NULL) { strcpy(p_data->nombre, "Rachel"); p_data->edad = 21; delete p_data; } // Reservacin de memoria para un arreglo de 10 registros o p_data = new Data[10]; if (p_data != NULL) // Verificacin de reservacin o o { // Lectura de datos del arreglo for (i = 0; i < 10; i++) cin >> p_data[i].nombre >> p_data[i].edad; // Liberacin de memoria del arreglo o delete [] p_data; } // Verificacin de reservacin o o // Inicializacin de datos o // en la memoria reservada // Liberacin de memoria o

14.

Bibliograf a

El presente art culo fue realizado con la ayuda del libro Programacin en C++, del autor Luis Joyanes o Aguilar, editorial Mc Graw Hill.