APUNTADORES
Los apuntadores en C y C++ son una herramienta muy potente de programación.Cuando los programadores cometen un error en su utilización, puede ser muy difícil encontrar el error, por lo cual es importante saber utilizarlos. El uso de apuntadores en C y C++ es muy importante debido a que permite hacer los programas más eficientes y más flexibles.
Cuando se declara una variable, el compilador reserva un espacio de memoria para ella y asocia el nombre de esta a la dirección de memoria desde donde comienzan los datos de esa variable.
Las direcciones de memoria se suelen describir como números en hexadecimal.
Un apuntador es una variable cuyo valor es la dirección de memoria de otra variable. Se dice que un apuntador “apunta” a la variable cuyo valor se almacena a partir de la dirección de memoria que contiene el apuntador.
DECLARACIÓN DE UN APUNTADOR
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>
Ejemplos:
CÓdigo C y C++
int *ptr1; // Apuntador a un dato de tipo entero (int)
char *cad1, *cad2; // Dos apuntadores a datos de tipo carácter (char)
float *ptr2; // Apuntador a un dato de tipo punto-flotante (float)
ASIGANCIÓN DE APUNTADORES
Se pueden asignar a un apuntador direcciones de variables a través del operador de referenciación (‘&’) o direcciones almacenadas en otros apuntadores.
Ejemplos:
int i = 5;
int *p, *q;
p = &i; // Se le asigna a ’p’ la dirección de ’i’
q = p; // Se le asigna a ’q’ la dirección almacenada en ’p’ (la misma de ’i’)
VERIFICACIÓN DE TIPOS DE 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)
APUNTADORES CONSTANTES Y APUNTADORES A CONSTANTES
Es posible declarar apuntadores constantes. De esta manera, no se permite la modificación de la dirección almacenada en el apuntador, pero sí se permite la modificación del valor al que apunta.
Ejemplo:
int x = 5, y = 7;
int *const p = &x; // Declaración e inicialización del apuntador constante
*p = 3; // Esto es váido
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
Apuntadores, arreglos y aritmetica de apuntadores
Los arreglos y apuntadores estan 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:
int lista_arr[5] = {10, 20, 30, 40, 50};
int *lista_ptr;
lista_ptr = lista_arr; // A partir de aqui ambas variables apuntan al mismo sitio
cout << lista_arr[0]; // Imprime 10
cout << lista_ptr[0]; // Instrucción equivalente a la anterior
cout << *lista_arr; // Instrucción equivalente a la anterior
cout << *lista_ptr; // Instrucción equivalente a la anterior
cout << lista_arr[3]; // Imprime 40
cout << lista_ptr[3]; // Instrucción equivalente a la anterior
Es posible sumar y restar valores enteros a un apuntador. El resultado de estas operaciones es el desplazamiento de la direccion de memoria hacia adelante (suma) o hacia atras (resta) por bloques de bytes del tamaño del tipo de dato apuntado por el apuntador.
Esto permite recorrer arreglos utilizando apuntadores.
Ejemplos:
int lista[5] = {10, 20, 30, 40, 50};
int *p;
char cad[15];
char *q;
p = &lista[3]; // ’p’ almacena la dirección de la posición 3 del arreglo
p = lista + 3; // Instrucción equivalente a la anterior
cout << lista[2]; // Imprime 30;
cout << *(lista+2); // Instrucción equivalente a la anterior
// Las siguientes instrucciones imprimen la palabra "Programando"
/* Nota: Recuerdese que una constante de cadena de caracteres es
una secuencia de caracteres en memoria seguidos del caracter nulo */
strcpy(cad, "Programando");
for (q = cad; *q != ’\0’; q++)
cout << q;
También es posible restar dos apuntadores. El resultado de esta operación es el número de bloques de bytes que hay entre las dos direcciones del tamaño del tipo de dato apuntado por los apuntadores.
Ejemplo:
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
Asignación dinámica de memoria
Los programas pueden crear variables globales o locales. Las variables declaradas globales en sus programas se almacenan en posiciones fijas 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 sólo mientras estan activas las funciones donde están declaradas. En ambos casos el espacio de almacenamiento se reserva en el momento de la compilacián del programa.
También es posible reservar y utilizar memoria dinámicamente, tomada de la zona de memoria llamada montículo (heap) o almacén libre. En C están disponibles varias funciones que permiten realizar reservar y librerar memoria, pero C++ además provee un método más fácil y seguro de hacerlo.