Actualizado el 14 de Julio del 2021 (Publicado el 8 de Enero del 2018)
1.091 visualizaciones desde el 8 de Enero del 2018
135,0 KB
9 paginas
Creado hace 20a (08/12/2004)
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.
Introducci´on
Los apuntadores en C y C++ son una herramienta muy potente de programaci´on que suele causar mucha
confusi´on en los estudiantes que la est´an aprendiendo. Adem´as, cuando los programadores cometen un error
en su utilizaci´on, puede ser muy dif´ıcil encontrar el error, por lo cual es importante saber utilizarlos muy
bien. El uso de apuntadores en C y C++ es muy importante debido a que permite hacer los programas m´as
eficientes y m´as flexibles. En en este art´ıculo se explica de una manera sencilla y breve todo lo referente a la
utilizaci´on de apuntadores tanto en C como en C++.
Todo lo explicado en este art´ıculo aplica tanto para C como para C++, a menos que se especifique un
lenguaje en particular. En algunos ejemplos de c´odigo que son aplicables a C aparecen instrucciones de
entrada y salida de las librer´ıas est´andar de C++.
2. Definici´on de apuntador
Cuando se declara una variable, el compilador reserva un espacio de memoria para ella y asocia el nombre
de ´esta a la direcci´on de memoria desde donde comienzan los datos de esa variable. Las direcciones de memoria
se suelen describir como n´umeros en hexadecimal.
Un apuntador es una variable cuyo valor es la direcci´on de memoria de otra variable. Se dice que un
apuntador “apunta” a la variable cuyo valor se almacena a partir de la direcci´on de memoria que contiene el
apuntador. Por ejemplo, si un apuntador p almacena la direcci´on de una variable x, se dice que “p apunta a
x”.
3. Referenciaci´on
La referenciaci´on es la obtenci´on de la direcci´on de una variable. En C y C++ esto se hace a trav´es del
operador ‘&’, aplicado a la variable a la cual se desea saber su direcci´on. N´otese que se trata de un operador
unario. Ejemplo:
int x = 25;
cout << "La direcci´on de x es: " << &x << endl;
C´odigo C y C++
Este c´odigo imprime un valor del estilo “0x4fffd34”. Este valor puede variar durante cada ejecuci´on del
programa, debido a que el programa puede reservar distintos espacios de memoria durante cada ejecuci´on.
4. Declaraci´on de apuntadores
Para declarar un apuntador se especifica el tipo de dato al que apunta, el operador ‘*’, y el nombre del
apuntador. La sintaxis es la siguiente:
<tipo de dato apuntado> *<indentificador del apuntador>
A continuaci´on se muestran varios ejemplos:
C´odigo C y C++
int *ptr1;
char *cad1, *cad2; // Dos apuntadores a datos de tipo car´acter (char)
float *ptr2;
// Apuntador a un dato de tipo punto-flotante (float)
// Apuntador a un dato de tipo entero (int)
5. Asignaci´on de apuntadores
Se pueden asignar a un apuntador direcciones de variables a trav´es del operador de referenciaci´on (‘&’) o
direcciones almacenadas en otros apuntadores. Ejemplos:
int i = 5;
int *p, *q;
C´odigo C y C++
p = &i;
q = p;
// Se le asigna a ’p’ la direcci´on de ’i’
// Se le asigna a ’q’ la direcci´on almacenada en ’p’ (la misma de ’i’)
6. Desreferenciaci´on de apuntadores
La desreferenciaci´on es la obtenci´on del valor almacenado en el espacio de memoria donde apunta un
apuntador. En C y C++ esto se hace a trav´es del operador ‘*’, aplicado al apuntador que contiene la
direcci´on del valor. N´otese que se trata de un operador unario. Ejemplos:
int x = 17, y;
int *p;
C´odigo C y C++
p = &x;
cout << "El valor de x es: " << *p << endl; // Imprime 17
y = *p + 3;
// A ’y’ se le asigna 20
C++ adem´as provee el operador binario ‘->’, utilizado para obtener campos de un registro con un apunta-
dor al mismo de una manera m´as f´acil y legible. Muchos compiladores de C tambi´en soportan este operador.
Ejemplo:
2
C´odigo C++
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. Verificaci´on de tipos en apuntadores
Al igual que el resto de las variables, los apuntadores se enlazan a tipos de datos espec´ıficos (apuntadores
a variables de cierto tipo), de manera que a un apuntador s´olo se le pueden asignar direcciones de variables
del tipo especificado en la declaraci´on del apuntador. Ejemplo:
int *p1;
float *p2;
int x;
C´odigo C y C++
p1 = &x; // Esto es v´alido
p2 = &x; // Esto no es v´alido (el compilador genera un error)
8. Direcciones inv´alidas y la direcci´on NULL
Normalmente, un apuntador inicializado adecuadamente apunta a alguna posici´on espec´ıfica de la memo-
ria. Sin embargo, algunas veces es posible que un apuntador no contenga una direcci´on v´alida, en cuyo caso
es incorrecto desreferenciarlo (obtener el valor al que apunta) porque el programa tendr´a un comportamiento
impredecible y probablemente err´oneo, aunque es posible que funcione bien. Un apuntador puede contener
una direcci´on inv´alida debido a dos razones:
1. Cuando un apuntador se declara, al igual que cualquier otra variable, el mismo posee un valor cualquiera
que no se puede conocer con antelaci´on, hasta que se inicialice con alg´un valor (direcci´on). Ejemplo:
float *p;
C´odigo C y C++
cout << "El valor apuntado por p es: " << *p << endl; // Incorrecto
*p = 3.5; // Incorrecto
2. Despu´es de que un apuntador ha sido inicializado, la direcci´on que posee puede dejar de ser v´alida si
se libera la memoria reservada en esa direcci´on, ya sea porque la variable asociada termina su ´ambito
o porque ese espacio de memoria fue reservado din´amicamente y luego se liber´o1. Ejemplo:
1La asignaci´on din´amica de memoria se explica en la secci´on 13
3
C´odigo C y C++
int *p, y;
void func()
{
int x = 40;
p = &x;
y = *p;
// Correcto
*p = 23; // Correcto
}
void main()
{
func();
// Incorrecto
y = *p;
*p = 25; // Incorrecto
}
Si se intenta desreferenciar un apuntador que contiene una direcci´on inv´alida pueden ocurrir cosas como
las siguientes:
Se obtiene un valor incorrecto en una o m´as variables debido a que no fue debidamente inicializada
la zona de memoria que se accede a trav´es de la direcci´on en cuesti´on. Esto puede ocasionar que el
programa genere resultados incorrectos.
Si casualmente la direcci´on es la misma de otra variable utilizada en el programa, o est´a dentro del
rango de direcciones de una zona de memoria utilizada, existe el riesgo de sobreescribir datos de otras
variables.
Existe la posibilidad de que la direcci´on est´e fuera de la zona de memoria utilizada para almacenar
datos y m´as bien est´e, por ejemplo, en la zona donde se almacenan las instrucciones del programa. Al
intentar escribir en dicha zona, f´acilmente puede ocurrir que el programa genere un error de ejecuci´on
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 direcci´on de memoria, en cuyo
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 definida en archivos de
cabecera como stdef.h y stdlib.h. Normalmente, en C++ se encuentra disponible sin incluir ning´un archivo
de cabecera. NULL se suele definir en estas librer´ıas as´ı:
#define NULL 0
C´odigo C y C++
Un apuntador nulo se utiliza para proporcionar a un programa un medio de conocer cu´ando un apuntador
contiene una direcci´on v´alida. Se suele utilizar un test condicional para saber si un apuntador es nulo o no
lo es, y tomar las medidas necesarias. El valor NULL es muy ´util para la construcci´on de estructuras de
datos din´amicas, como las listas enlazadas, matrices esparcidas, etc. Es igualmente incorrecto desreferenciar
el valor NULL por las mismas razones presentadas previamente.
9. Apuntadores a apuntadores
Dado que un apuntador es una variable que apunta a otra, f´acilmente se puede deducir que pueden existir
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:
C´odigo C y C++
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 modificaci´on de la direcci´on
almacenada en el apuntador, pero s´ı se permite la modificaci´on del valor al que apunta. Ejemplo:
int x = 5, y = 7;
int *const p = &x; // Declaraci´on e inicializaci´on del apuntador constante
*p = 3;
p = &y;
// Esto es v´alido
// Esto no es v´alido (el compilador genera un error)
C´odigo C y C++
Tambi´en es posible declarar apuntadores a datos constantes. Esto hace que no sea posible modificar el
valor al que apunta el apuntador. Ejemplo:
C´odigo C y C++
int x = 5, y = 7;
const int *p = &x; // Declaraci´on e inicializaci´on del apuntador a constante
p = &y;
*p = 3;
y = 3;
// Esto es v´alido
// Esto no es v´alido (el compilador genera un error)
// Esto es v´alido
11. Apuntadores, arreglos y aritm´etica de apuntadores
Los arreglos y apuntadores est´an fuertemente relacionados. El nombre de un arreglo es simplemente
un apuntador constante al inicio del arreglo. Se pueden direccionar arreglos como si fueran apuntadores y
apuntadores como si fueran arreglos. Ejemplos:
C´odigo C y C++
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];
cout << lista_ptr[0];
cout << *lista_arr;
cout << *lista_ptr;
cout << lista_arr[3];
cout << lista_ptr[3];
// Imprime 10
// Instrucci´on equivalente a la anterior
// Instrucci´on equivalente a la anterior
// Instrucci´on equivalente a la anterior
// Imprime 40
// Instrucci´on equivalente a la anterior
Es posible sumar y res
Comentarios de: Apuntadores en C y C++ (0)
No hay comentarios