Presentación Apuntadores
Presentación Apuntadores
Presentación Apuntadores
2. Apuntadores
2.1. Definición de Apuntador
Llamados también punteros. Un Apuntador es una variable que contiene una dirección
de memoria, la cual corresponderá a un dato o a una variable que contiene el dato. Cada
variable que se utiliza en una aplicación ocupa una o varias posiciones de memoria. Estas
posiciones de memoria se accesan por medio de una dirección.
Cada variable que se utiliza en una aplicación ocupa una o varias posiciones de memoria.
Estas posiciones de memoria se accesan por medio de una dirección.
Representación:
2.2. Operador de dirección de
memoria (&)
Una dirección de memoria contiene un byte de información, una variable dependiendo de su tipo
puede ocupar uno o más bytes. Un apuntador solo almacena la dirección del primer byte de la
variable sin importar que esta sea de más de 1 byte.
Para poder ocupar los apuntadores además de la dirección de memoria que estos almacenan, se
necesita poder manipular los datos a los cuales estos apuntan.
El Operador de Dirección ( &) regresa la dirección de una variable. El operador & devuelve la
dirección de cualquier variable aunque no sea de tipo apuntador. Por ejemplo:
int x = 25;
cout << "La dirección de x es: " << &x << endl;
Este código imprime un valor del estilo “0x4fffd34”. Este valor puede variar durante cada ejecución
del programa, debido a que el programa puede reservar distintos espacios de memoria durante
cada ejecución.
2.3. Operador de indirección de
memoria (*)
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++ además provee el operador binario ‘->’, utilizado para obtener campos de un
registro con un apuntador al mismo de una manera más fácil y legible. Muchos
compiladores de C también soportan este operador.
Ejemplo:
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 ’->’
6
Declaración de apuntadores.
Tipo_dato : Especifica el tipo de objeto apuntado y puede ser cualquier tipo (int, float, char, etc).
Ejemplos de declaración:
7
Ejemplos:
Ejemplo 2.
9
Ejemplo: Probar si las siguientes expresiones son verdaderas o falsas, suponiendo que:
10
Verificación 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ólo
se le pueden asignar direcciones de variables del tipo especificado en la declaración del
apuntador. Ejemplo:
int *p1;
float *p2;
int x;
p1 = &x; // Esto es válido
p2 = &x; // Esto no es válido (el compilador genera un error)
11
2.3. Operador NULL
Sin embargo, algunas veces es posible que un apuntador no contenga una dirección válida, en cuyo
caso es incorrecto desreferenciarlo (obtener el valor al que apunta) porque el programa tendrá un
comportamiento impredecible y probablemente erróneo, aunque es posible que funcione bien. Un
apuntador puede contener una dirección inválida 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ón, hasta que se inicialice con algún valor
(dirección). Ejemplo:
float *p;
cout << "El valor apuntado por p es: " << *p << endl; // Incorrecto
*p = 3.5; // Incorrecto
2. Después de que un apuntador ha sido inicializado, la dirección que posee puede dejar de ser
válida si se libera la memoria reservada en esa dirección, ya sea porque la variable asociada termina
su ámbito o porque ese espacio de memoria fue reservado dinámicamente y luego se liberó.
Ejemplo:
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
}
13
Si se intenta desreferenciar un apuntador que contiene una dirección inválida pueden ocurrir
cosas como las siguientes:
Se obtiene un valor incorrecto en una o más variables debido a que no fue debidamente
inicializada la zona de memoria que se accede a través de la dirección en cuestión. Esto puede
ocasionar que el programa genere resultados incorrectos.
Existe la posibilidad de que la dirección esté fuera de la zona de memoria utilizada para
almacenar datos y más bien esté, por ejemplo, en la zona donde se almacenan las
instrucciones del programa. Al intentar escribir en dicha zona, fácilmente puede ocurrir que el
programa genere un error de ejecución y el sistema operativo lo detenga, o que el programa
no responda y deje al sistema operativo inestable.
#define NULL O
15
Apuntadores y Matrices
Considerar:
int a[10][10];
int *b[10];
El uso de a y b puede ser parecido, desde el momento en que a[5][5] y b[5][5] son
referencias validas a un int.
Sin embargo a b la declaración solo le asigna 10 apuntadores, cada uno de los cuales
deberá de apuntar a un arreglo de enteros.
16
Diferentes declaraciones
int a[10][20];
int *a[10];
int **a;
int (* a)[20];
17
Apuntadores y cadenas
Sea la declaración:
Cada cadena está almacenada en memoria como una cadena de caracteres terminada en NULL (\0).
En el arreglo no están colocadas las cadenas, tan solo están almacenados los apuntadores.
Aunque el arreglo es de tamaño fijo, permite el acceso a cadenas de caracteres de cualquier longitud ( por
ejemplo la longitud de Bye es más corta que la de saludos).
void main()
{
char *esp[10] = { ''uno'', ''dos'', ''tres'‘ };
char frances[5][10] = { ''un'',''deux'', ''trois'' };
printf(''Elemento 3 entrada 2 esp: %c \n'',esp[2][3]);
printf(''Elemento 4 entrada 3 frances: %c \n'',frances[3][4]);
printf(''Elemento 7 entrada 2 esp: %c \n'',esp[2][7]);
frances[3][4]='A';
printf(''Elemento 4 entrada 3 frances: %c \n'',frances[3][4]);
esp[2][3]='A';
printf(''Elemento 3 entrada 2 esp: %c \n'',esp[2][3]);
printf(“Cadena esp %s \n” ,esp);
printf(“Cadena frances %s \n”,frances[1]);
getch();
}
18
2.5. Apuntador de apuntadores
Dado que un apuntador es una variable que apunta a otra, fácilmente 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:
char c = ’z’;
char *pc = &c;
char **ppc = &pc;
char ***pppc = &ppc;
***pppc = ’m’; // Cambia el valor de c a ’m’
Apuntadores constantes y apuntadores a constantes
int x = 5, y = 7;
int *const p = &x; // Declaración e inicialización del apuntador constante
*p = 3; // Esto es válido
p = &y; // Esto no es válido (el compilador genera un error)
También es posible declarar apuntadores a datos constantes. Esto hace que no sea
posible modificar el valor al que apunta el apuntador. Ejemplo:
int x = 5, y = 7;
const int *p = &x; // Declaración e inicialización del apuntador a constante
p = &y; // Esto es válido
*p = 3; // Esto no es válido (el compilador genera un error)
y = 3; // Esto es válido
20
Asignación Dinámica de Memoria.
Para la asignación dinámica de memoria existe una función llamada malloc que se
encuentra en la librería stdlib.h.
21
Asignación de memoria a valores tipo char
22
Asignación de memoria a valores numéricos.
Si se requiere reservar memoria para un tipo de dato que no es char se realiza de la siguiente manera:
Ejemplos:
int *apun;
float *apun;
23
2.6. Enviar y recibir apuntadores en
una función
Cuando pasamos un apuntador como parámetro de una función por valor pasa lo
mismo que con cualquier otro objeto.
Dentro de la función trabajamos con una copia del parámetro, que en este caso es un
apuntador. Por lo tanto, igual que pasaba con el ejemplo anterior, las modificaciones
en el valor del parámetro serán locales a la función y no se mantendrán después de
retornar.
Sin embargo, no sucede lo mismo con el objeto apuntado por el apuntador, puesto
que en ambos casos será el mismo, ya que tanto el apuntador como el parámetro
tienen como valor la misma dirección de memoria. Por lo tanto, los cambios que
hagamos en los objetos apuntados por el apuntador se conservarán al abandonar la
función.
Ejemplo:
#include <iostream>
using namespace std;
void funcion(int *q);
int main()
{
int a;
int *p;
a = 100;
p = &a;
// Llamamos a función con un apuntador
funcion(p); // (1)
cout << "Variable a: " << a << endl;
cout << "Variable *p: " << *p << endl; // Llamada a función con la dirección de "a" (constante)
funcion(&a); // (2)
cout << "Variable a: " << a << endl;
cout << "Variable *p: " << *p << endl;
return 0;
}
void funcion(int *q)
{
// Cambiamos el valor de la variable apuntada por
// el apuntador
*q += 50;
q++;
}
25
Dentro de la función se modifica el valor apuntado por el apuntador, y los cambios
permanecen al abandonar la función. Sin embargo, los cambios en el propio
apuntador son locales, y no se conservan al regresar.
Análogamente a como lo hicimos antes al pasar una constante literal, podemos pasar
apuntador variables o constantes como parámetro a la función. En (1) usamos un
variable de tipo apuntador, en (2) usamos un apuntador constante.
De modo que con este tipo de declaración de parámetro para función estamos
pasando el apuntador por valor. ¿Y cómo haríamos para pasar un apuntador por
referencia?:
26
Nota: En C no existen referencias de este tipo, y la forma de pasar parámetros por
referencia es usar un apuntador. Por supuesto, para pasar una referencia a un
apuntador se usa un apuntador a apuntador, etc.
Por ejemplo, supongamos que necesitamos pasar como parámetro a una función un
objeto que ocupe varios miles de bytes. Si se pasa por valor, en el momento de la
llamada se debe copiar en la pila todo el objeto, y la función recupera ese objeto de la
pila y se lo asigna al parámetro. Sin embargo, si se usa una referencia, este paso se
limita a copiar una dirección de memoria.
El lenguaje C no provee una manera de pasar parámetros por referencia. Sin embargo, es posible
hacerlo a través del uso de apuntadores. A continuación se muestra un ejemplo del paso de un
parámetro por referencia en C++, y luego un código equivalente en C o C++ utilizando un
apuntador:
void main()
{
int x;
suma(7, 5, x);
cout << "7 + 5 = " << x;
}
Nótese que en ambos casos se utiliza el operador ‘&’ para cosas distintas. El operador ‘&’ tiene
dos significados como operador unario: señalación de parámetro por referencia y operador de
referenciación.
28
Bibliografía
“Programación en C++”, del autor Luis Joyanes Aguilar, editorial Mc Graw Hill.
http://aprendeenlinea.udea.edu.co/lms/men_udea/pluginfile.php/25669/mod_reso
urce/content/0/documentos/Apuntadores.pdf
http://www.utm.mx/~mgarcia/PE7(Apuntadores).pdf
http://profesores.fi-b.unam.mx/m3615m/Documentos/apuntadores%20en%20C.pdf
http://c.conclase.net/curso/?cap=015b
https://www.fing.edu.uy/tecnoinf/mvd/cursos/eda/material/teo/EDA-teorico7.pdf