Escolar Documentos
Profissional Documentos
Cultura Documentos
Un apuntador o puntero es una variable que contiene una dirección de memoria, la cual
corresponderá a un dato o a una variable (que contiene el dato). Quiere decir que el
puntero apunta al espacio físico donde esta el dato o la variable.
Puntero
p 2049
Direcciones de Memoria
2047 2048 2049 2050 2051
1 #include <stdio.h>
2 int main()
3 { int x = 5, y = 7;
4 int *const p = &x; // Declaración e inicialización del
5 apuntador constante
6 *p = 3; // Esto es valido
7 p = &y; // Esto no es válido (el compilador genera un
8 error)
9 }
También es posible declarar apuntadores a datos constantes. Esto hace que no sea posible
modificar el valor al que apunta el apuntador. Ejemplo:
1 #include <stdio.h>
2 int main()
3 { int x = 5, y = 7;
4 const int *p = &x; // Declaración e inicialización del
5 apuntador a constante
6 p = &y; // Esto es válido
7 *p = 3; // Esto no es válido (el compilador genera un
8 error)
9 y = 3; // Esto es válido}
Apuntadores y arreglos
Los arreglos y apuntadores están 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.
Cualquier operación que pueda lograrse por indexación de un arreglo también puede
realizarse con apuntadores.
Es posible sumar y restar valores enteros a un apuntador. El resultado de estas operaciones
es el desplazamiento de la dirección de memoria hacia adelante (suma) o hacia atrás
(resta) por bloques de bytes del tamaño del tipo de dato apuntado por el apuntador. Esto
permite recorrer arreglos utilizando apuntadores.
El siguiente ejemplo se muestra la relación entre apuntadores y arreglos, en este se declara
un dato estructurado tipo vector, y se declara una variable de tipo apuntador que hace
referencia a las localidades de memoria del vector, y se observa como no es necesario
hacer uso de los índices del arreglo, para realizar el recorrido del dato estructurado
(vector).
int a[10] ; // Declaración Vector - define un arreglo de tamaño 10, o sea un bloque de
10 enteros consecutivos que se acceden a través de a[0], a[1], . . . , a[9].
a:
a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
notación a[i] hace referencia al i-esimo elemento del arreglo. Supongamos que pa es un
puntero a enteros, declarado como
int *pa; // Declaración Puntero
entonces la asignación:
pa = &a[0]; // Inicialización del puntero - hace que pa apunte al elemento cero de a, o
sea pa contiene la dirección de a[0]. El nombre de un arreglo es un puntero a su primer
elemento (con lo cual podemos escribir el ejemplo anterior como pa = a). Si pa apunta a
un elemento particular de un arreglo, entonces por definición pa+1 apunta al siguiente
elemento, pa+i apunta i elementos más adelante y pa-1 apunta i elementos antes. Por lo
tanto si pa apunta a a[0],
*(pa + 1); // hace referencia al contenido de a[1], pa+i es la dirección de a[i] y *(pa+i)
es el contenido de a[i].
pa: pa+1:pa+2:
.
a:
a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
Por ejemplo
Un string escrito como “Soy una cadena”. Es un arreglo de caracteres. El arreglo esta
determinado por el carácter nulo ‘\0’ para que el programa puede encontrar el fin.
Char *pmessage; // Declaración de la variable (apuntador) pmessage
pmessage = “ya es el tiempo”; // Asigna a pmessage un apuntador al arreglo de
caracteres.
Existe una importante diferencia entre estas definiciones:
Char amessage [] = “ya es el tiempo”; /*arreglo */
Char *pmessage= “ya es el tiempo”; /*apuntador*/
amessage es un arreglo suficientemente grande como para contener la secuencia de
caracteres y el ‘/0’ que lo inicializa. Se pueden modificar caracteres individuales dentro
del arreglo, pero amessage siempre se referirá a la misma localidad de almacenamiento.
Por otro lado, pmessage es un apuntador, inicializado para apuntar a una cadena
constante; el apuntador puede modificarse posteriormente para que apunte a algún otro
lado, pero el resultado es indefinido si trata de modificar el contenido de la cadena
Arreglo de punteros
Un array multidimensional puede ser expresado como un array de punteros en lugar de
como un puntero a un grupo contiguo de arrays. En estos casos el nuevo array será de una
dimensión menor que el array multidimensional. Cada puntero indicará el principio de un
array de dimensión (n-1).
En términos generales, un array bidimensional puede ser definido como un array
unidimensional de punteros. Los punteros pueden ser almacenados en arreglos por el
hecho de ser variables.
Los arreglos de punteros son comunes cuando se desea tener un array de cadenas de
caracteres. Si una cadena de caracteres puede escribirse como char *c; un array de
cadenas de caracteres podrá escribirse como char **c; o char *c[x];.
Para ilustrar este hecho vamos a ver un ejemplo de un programa que ordena las líneas de
texto en orden alfabético; haciendo uso de un arreglo de punteros
. defghi . defghi
. jklmnopqrst . jklmnopqrst
. abc . abc
Dos líneas pueden ser comparadas pasando sus punteros a strcmp. Cuando dos líneas
desordenadas tienen que intercambiarse, se intercambian los apuntadores en el arreglo de
apuntadores, no las líneas de texto. Esto elimina el doble problema de un manejo
complicado de almacenamiento y exceso de procesamiento que se produciría al mover
las líneas.
Este segundo ejemplo, muestra un arreglo o vector de punteros y se observa como cada
elemento del arreglo apunta a una localidad de memoria perteneciente a una variable
diferente.
. Mes ilegal \0
. Ene \0
. Feb \0
. Mar \0
aname:
1 #include <stdio.h>
2 int main(){
3 int x= 14;
4 inc(&x);
5 ...
6 void inc (int *par) {
7 (*par)++;
8 }
9 }
Resultado
x: 5 y:10
x:10 y:5
Donde vemos que esta vez sí se modificó el contenido de las variables en cuestión, ya que
se da acceso no solo al parámetro si no a la localidad de memoria.
Puntero a funciones
Un puntero a función es una variable que almacena la dirección de una función. Esta
función puede ser llamada más tarde, a través del puntero. Este tipo de construcción es
útil pues encapsula comportamiento, que puede ser llamado a través de un puntero.
Veamos cómo funciona mediante un ejemplo sencillo que crea un puntero a una función
de imprimir y lo invoca:
1 #include <stdio.h>
2 void imprime()
3 {
4 printf("Imprimiendo un message\n");
5 }
6 int main()
7 {
8 void (*ptr_funct)(void)=imprime;
9 ptr_funct(); //Llama a imprime
10 return 0;
11 }
Devolución de punteros
Una función puede retornar un tipo de datos puntero. La función se declararía así tipo
*función (argumentos).
Este tipo de funciones se suelen usar para reservar memoria o crear elementos en
estructuras dinámicas de datos.
Apuntadores y gestión dinámica de memoria
La gestión de memoria dinámica se refiere a la gestión de memoria manual. Esto le
permite obtener más memoria cuando sea necesario y liberarla cuando no sea necesario.
Aunque en lenguaje C, de forma inherente no tiene ninguna técnica para asignar memoria
dinámicamente, hay 4 funciones de biblioteca definidas en <stdlib.h> para la asignación
dinámica de memoria, usando malloc() (memory allocate = asignar memoria) , calloc(),
o cualquier otra función de reservación de memoria en tiempo de ejecución
Usar este método (malloc), permite posponer la decisión del tamaño del bloque de
memoria necesario para guardar, por ejemplo, un arreglo, hasta el tiempo de ejecución; o
permite usar una sección de la memoria para guardar un arreglo de enteros en un tiempo
determinado, y posteriormente, cuando esa memoria no sea necesaria, liberarla para otros
usos (método: free), como para guardar un arreglo de estructuras.
void *malloc (size_t_size) // El argumento de la función malloc especifica el número de
bytes de memoria que el usuario quiere reservar y devuelve la dirección de memoria de
la zona de memoria reservada.
En forma similar se puede lograr liberar la memoria, la función free permite liberar un
espacio de memoria gestado por la función malloc. Para realizar esta operación, sólo hay
que pasarle como parámetro da dirección de inicio del bloque. El prototipo de la esta
función se muestra a continuación:
void *free (void *ptr) // libera la memoria apuntada por ptr, ptr puede ser de cualquier
tipo. Los bloques a liberar debieron ser asignados de manera dinámica.
1 #include <stdio.h>
2 #include <alloc.h>
3 int main (void){
4 int * pt;
5 int numElem;
6 printf("Ingrese el número de datos del arreglo: ");
7 scanf("%d%", &numElem);
8 pt = (int *) malloc( numElem * sizeof(int) );
9 pt[2] = 177;
10 *pt = 88;
11 *(pt+2) = 92;
12 free(pt); // el bloque fue liberado ···
13 }