UNIDAD 6 Apuntadores

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 muyC 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++.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++.
Apuntadores

Los apuntadores en
bien. El uso de apuntadores en
Todo lo explicado en este art´ıculo aplica tanto para

Definici´on de apuntador p almacena la direcci´on de una variable x, se dice que “p apunta a
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

x ”. 
Referenciacion
La referenciaci´on es la obtenci´on de la direcci´on de una variable. En
C´odigo C y C++ int x = 25;
cout << "La direcci´on de x es: " << &x << endl;
Este codigo 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 ejecucion.

Declaracion 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; // Apuntador a un dato de tipo entero (int)
char *cad1, *cad2; // Dos apuntadores a datos de tipo car´acter (char)
float *ptr2; // Apuntador a un dato de tipo punto-flotante (float)


Asignacion 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:

C´odigo
C y C++
int i = 5;
int *p, *q;
p = &i; // Se le asigna a ’p’ la direcci´on de ’i’
q = p; // Se le asigna a ’q’ la direcci´on almacenada en ’p’ (la misma de ’i’)

Desreferenciaci´on de apuntadores
La desreferenciaci´on es la obtenci´on del valor almacenado en el espacio de memoria donde apunta un apuntador. En

Codigo
C y C++
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++
Ejemplo:

Codigo
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 ’->’

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:

Codigo
C y C++
int *p1;
float *p2;
int x;
p1 = &x; // Esto es v´alido
p2 = &x; // Esto no es v´alido (el compilador genera un error)

Direcciones inv´alidas y la direcci´on
NULL
Normalmente, un apuntador inicializado adecuadamente apunta a alguna posici´on espec´ıfica de la memoria.
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:

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:

Codigo
C y C++
float *p;
cout << "El valor apuntado por p es: " << *p << endl; // Incorrecto
*p = 3.5; // Incorrecto

Despues 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´o
1. Ejemplo:

Codigo
C y C++
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 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
dice que el apuntador es nulo (no apunta a nada).


Codigo
C y C++#define NULL 0

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
lo es, y tomar las medidas necesarias. El valor

 
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:

Codigo
C y C++
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
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:

Codigo
C y C++
int x = 5, y = 7;
int *const p = &x; // Declaraci´on e inicializaci´on del apuntador constante
*p = 3; // Esto es v´alido
p = &y; // Esto no es v´alido (el compilador genera un error)

Tambien es posible declarar apuntadores a datos constantes. Esto hace que no sea posible modificar el valor al que apunta el apuntador. Ejemplo:

Codigo
C y C++
int x = 5, y = 7;
const int *p = &x; // Declaraci´on e inicializaci´on del apuntador a constante
p = &y; // Esto es v´alido
*p = 3; // Esto no es v´alido (el compilador genera un error)
y = 3; // Esto es v´alido

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:

Codigo
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]; // Imprime 10
cout << lista_ptr[0]; // Instrucci´on equivalente a la anterior
cout << *lista_arr; // Instrucci´on equivalente a la anterior
cout << *lista_ptr; // Instrucci´on equivalente a la anterior
cout << lista_arr[3]; // Imprime 40
cout << lista_ptr[3]; // Instrucci´on equivalente a la anterior

Es posible sumar y restar valores enteros a un apuntador. El resultado de estas operaciones es el desplazamiento de la direcci´on de memoria hacia adelante (suma) o hacia atr´as (resta) por bloques de bytes del tama˜no del tipo de dato apuntado por el apuntador. Esto permite recorrer arreglos utilizando apuntadores.

test condicional para saber si un apuntador es nulo o noNULL 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.
NULL, en cuyo caso seNULL 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ı:
adem´as provee el operador binario ‘->’, utilizado para obtener campos de un registro con un apuntador al mismo de una manera m´as f´acil y legible. Muchos compiladores de C tambi´en soportan este operador.
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:
 
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: