You are on page 1of 46

1

PUNTEROS
2
Qu es un PUNTERO?:
Un puntero es un objeto que apunta a otro objeto. Es decir,
una variable cuyo valor es la direccin de memoria de otra
variable.
No hay que confundir una direccin de memoria con el
contenido de esa direccin de memoria.
int x = 25;

... ... 25 ... ... ... ...
Direccin 1502 1504 1506 1508
La direccin de la variable x (&x) es 1502
El contenido de la variable x es 25
3
Las direcciones de memoria dependen de la arquitectura
del computador y de la gestin que el sistema operativo
haga de ella.
En lenguaje ensamblador se debe indicar numricamente
la posicin fsica de memoria en que queremos almacenar un
dato. De ah que este lenguaje dependa tanto de la mquina
en la que se aplique.
En C no debemos, ni podemos, indicar numricamente la
direccin de memoria, si no que utilizamos una etiqueta que
conocemos como variable (en su da definimos las variables
como direcciones de memoria). Lo que nos interesa es
almacenar un dato, y no la localizacin exacta de ese dato
en memoria.
4
Una variable puntero se declara como todas las variables.
Debe ser del mismo tipo que la variable apuntada. Su
identificador va precedido de un asterisco (*):
int *punt;
Es una variable puntero que apunta a variable que contiene
un dato de tipo entero llamada punt.
char *car:
Es un puntero a variable de tipo carcter.
long float *num;
float *mat[5]; // . . .
Un puntero
tiene su
propia
direccin de
memoria:
&r
5
Es decir: hay tantos tipos de punteros como tipos de
datos, aunque tambin pueden declararse punteros a
estructuras ms complejas (funciones, struct, Archivos...) e
incluso punteros vacos (void ) y punteros nulos (NULL).
Declaracin de variables puntero: Sea un fragmento de
programa en C:




char dato; //variable que almacenar un carcter.
char *punt; //declaracin de puntero a carcter.
punt = &dato; //en la variable punt guardamos la direccin
// de memoria de la variable dato; punt apunta
// a dato. Ambas son del mismo tipo, char.



6
int *punt = NULL, var = 14;
punt = &var;
printf(%#X, %#X, punt, &var) //la misma salida: direccin
printf(\n%d, %d, *punt, var); //salida: 14, 14
Hay que tener cuidado con las direcciones apuntadas:
printf(%d, %d, *(punt+1), var+1);
*(punt + 1) repesenta el valor contenida en la direccin
de memoria aumentada en una posicin (int=2bytes), que
ser un valor no deseado. Sin embargo var+1 representa
el valor 15.
punt + 1 representa lo mismo que &var + 1 (avance en la
direccin de memoria de var).


7
Al trabajar con punteros se emplean dos operadores
especficos:
Operador de direccin: & Representa la
direccin de memoria de la variable que le sigue:
&fnum representa la direccin de fnum.
Operador de contenido o indireccin: *
El operador * aplicado al nombre de un puntero
indica el valor de la variable apuntada:
float altura = 26.92, *apunta;
apunta = &altura; //inicializacin del puntero

8
float altura = 26.92, *apunta;
apunta = &altura; //inicializacin del puntero
.printf(\n%f, altura); //salida 26.92
.printf(\n%f, *apunta);
No se debe confundir el operador * en la declaracin
del puntero:
int *p;
Con el operador * en las instrucciones:
. *p = 27;
printf(\nContenido = %d, *p);
9
Veamos con un ejemplo en C la diferencia entre
todos estos conceptos
Es decir: int x = 25, *pint;
pint = &x;

La variable pint contiene la direccin de memoria de
la variable x. La expresin: *pint representa el valor
de la variable (x) apuntada, es decir 25. La variable
pint tambin tiene su propia direccin: &pint

10
Veamos con otro ejemplo en C la diferencia entre
todos estos conceptos
void main(void) {
int a, b, c, *p1, *p2;
void *p;
p1 = &a; // Paso 1. La direccin de a es asignada a p1
*p1 = 1; // Paso 2. p1 (a) es igual a 1. Equivale a a = 1;
p2 = &b; // Paso 3. La direccin de b es asignada a p2
*p2 = 2; // Paso 4. p2 (b) es igual a 2. Equivale a b = 2;
p1 = p2; // Paso 5. El valor del p1 = p2
*p1 = 0; // Paso 6. b = 0
11
p2 = &c; // Paso 7. La direccin de c es asignada a p2
*p2 = 3; // Paso 8. c = 3
printf("%d %d %d\n", a, b, c); // Paso 9. Qu se imprime?
p = &p1; // Paso 10. p contiene la direccin de p1
*p = p2; // Paso 11. p1= p2;
*p1 = 1; // Paso 12. c = 1
printf("%d %d %d\n", a, b, c); // Paso 13. Qu se imprime?
}

12
Vamos a hacer un seguimiento de las direcciones de
memoria y de los valores de las variables en cada paso.
Suponemos que la variable a es colocada en la direccin
0000, b en la siguiente, es decir 0002, con un offset de 2
bytes, por ser valores integer.
Se trata de un sistema de posiciones relativas de memoria.
Se ver en aritmtica de punteros.
Se obtiene el siguiente cuadro. En l reflejamos las
direcciones relativas de memoria y los cambios en cada uno
de los pasos marcados:

13
Paso
a
0000

b
0002

c
0004
p1
0006
p2
0008
p
0010
1
0000
2
1 0000
3
1 0000 0002
4
1 2 0000 0002
5
1 2 0002 0000
6
1 0 0002 0002
7
1 0 0002 0004
8
1 0 3 0002 0004
9
1 0 3 0002 0004
10
1 0 3 0002 0004 0006
11
1 0 3 0004 0004 0006
12
1 0 1 0004 0004 0006
13
1 0 1 0004 0004 0006
14
Inicializacin de punteros(I):


Si <Almacenamiento> es extern o static, <Expresion>
deber ser una expresin constante del tipo <Tipo>
expresado.
Si <Almacenamiento> es auto, entonces <Expresion> puede
ser cualquier expresin del <Tipo> especificado.
Ejemplos:
1) La constante entera 0, NULL (cero) proporciona un
puntero nulo a cualquier tipo de dato:
int *p;
p = NULL; //actualizacin

< Almacenamiento > < Tipo > * < Nombre > = < Expresin >
15
Inicializacin de punteros(II):
2) El nombre de un array de almacenamiento static o
extern se transforma segn la expresin:
a) float mat[12];
float *punt = mat;
b) float mat[12];
float *punt = &mat[0];
3) Un cast puntero a puntero:
int *punt = (int *) 123.456;
Inicializa el puntero con el entero.

16
Inicializacin de punteros(III):
4) Un puntero a carcter puede inicializarse en la forma:
char *cadena = Esto es una cadena;
5) Se pueden sumar o restar valores enteros a las
direcciones de memoria en la forma: (aritmtica de
punteros) int x;
int *punt = &x+2, *p = &x-1;
6) Equivalencia: Dos tipos definidos como punteros a
objeto P y puntero a objeto Q son equivalentes slo si
P y Q son del mismo tipo. Aplicado a matrices:
nombre_puntero = nombre_matriz;
17
PUNTEROS Y ARRAYS
Sea el array de una dimensin:
int mat[ ] = {2, 16, -4, 29, 234, 12, 0, 3};
en el que cada elemento, por ser tipo int, ocupa dos bytes
de memoria.
Suponemos que la direccin de memoria del primer
elemento, es 1500:
&mat[0] es 1500
&mat[1] ser 1502
&mat[7] ser 1514

18
PUNTEROS Y ARRAYS
int mat[ ] = {2, 16, -4, 29, 234, 12, 0, 3};
En total los 8 elementos ocupan 16 bytes.
Podemos representar las direcciones de memoria que
ocupan los elementos del array , los datos que contiene y
las posiciones del array en la forma:

Direccin 1502 1504 1506 1508 1510 1512 1514
2 16 -4 29 234 12 0 3
Elemento mat[1] mat[2] mat[3] mat[4] mat[5] mat[6] mat[7]
19
Direccin 1502 1504 1506 1508 1510 1512 1514
El acceso podemos hacerlo mediante el ndice:
x = mat[3]+mat[5]; // x = 29 + 12
para sumar los elementos de la cuarta y sexta posiciones.
Como hemos dicho que podemos acceder por posicin y
por direccin: Es lo mismo &mat[0] y mat?
Y &mat[1] = mat++ ?
Veamos el cdigo de un ejemplo:
2 16 -4 29 234 12 0 3
Elemento mat[1] mat[2] mat[3] mat[4] mat[5] mat[6] mat[7]

20
#include <stdio.h>
#include <conio.h>
int mat[5]={2, 16, -4, 29, 234, 12, 0, 3}, i; //declaradas como globales
void main() {
printf("\n%d", &mat[0]); //resultado: 1500 (direccin de mem)
printf("\n%p", mat); //resultado: 1500 ( " " " " " )
i++; //i=1
printf("\n%p", mat+i); //resultado: 1502 ( " " " " " )
printf("\n%d", *(mat+i)); //resultado: 16 (valor de mat[1] o valor
getch(); } //en la direccin 1502
21
Comprobamos con un ejemplo:

Parece deducirse que accedemos a los elementos del
array de dos formas:
- mediante el subndice.
- mediante su direccin de memoria.

2 16 -4 29 234 12 0 3
Elemento mat[1] mat[2] mat[3] mat[4] mat[5] mat[6] mat[7]
22
Analizando las direcciones de memoria del array:
2 16 -4 29 234 12 0 3
&mat[0] &mat[1] &mat[2] &mat[3] &mat[4] &mat[5] & mat[6] &mat[7]

mat mat+1 mat+2 mat+3 mat+4 mat+5 mat+6 mat+7
Direccin
del elemento
0
Direccin del
octavo elemento
Puntero a la
direccin del
elemento 0
Incremento en una
unidad int (dos bytes)
mat++
23
De lo anterior se obtienen varias conclusiones:
- Es lo mismo &mat[0] que mat, &mat[2] que mat + 2
- Para pasar de un elemento al siguiente, es lo mismo:
for(i=0; i<8; i++)
printf(&mat [%d] = %p, i, &mat[i]);
que el cdigo:
for(i=0; i<8; i++)
printf(mat + %d = %p, i, mat + = i);
A esta forma de desplazarse en memoria se le llama
Aritmtica de punteros
24

25

Aritmtica de punteros (I):
-A una variable puntero se le puede asignar la direccin de
cualquier objeto.
-A una variable puntero se le puede asignar la direccin de
otra variable puntero (siempre que las dos sealen el mismo
objeto)
-A un puntero se le puede inicializar con el valor NULL
-Una variable puntero puede ser restada o comparada con
otra si ambas apuntan a elementos de un mismo array.
26

Aritmtica de punteros (II):
- Se puede sumar o restar valores enteros : p++, pv+3,
teniendo en cuenta que el desplazamiento (offset) depende
del tipo de dato apuntado:
p++; //p apunta a la siguiente direccin
pv+=3 // pv apunta 3*n bytes del dato apuntado (offset)
Si tenemos:
float *decimal; //suponemos que apunta a 0000
decimal++; //apunta a 0004

27

Aritmtica de punteros (III):
-Observar las siguientes instrucciones:
int *p;
double *q;
void *r; //puntero genrico
p = &34; // las constantes no tienen direccin
p = &(i+1); // las expresiones no tienen direccin
&i = p; // las direcciones no se pueden cambiar
p = q; // ilegal
p = (int *)q; // legal

28
Utilizando la aritmtica de punteros nos desplazamos
de unas posiciones de memoria a otras. Pero. cmo acceder
a los contenidos de esas posiciones utilizando notacin de
punteros?
2 16 -4 29 234 12 0 3
mat[0] mat[1] mat[2] mat[3] mat[4] mat[5] mat[6] mat[7]

*mat *(mat+1) *(mat+2) *(mat+4) *(mat+6)
mat[0] = 2
*(mat+3) *(mat+5)
Empleamos el operador *, indireccin que nos da
el contenido de la direccin de memoria apuntada.
*mat = 2 *(mat+7) = 3
mat[7] = 3
29

Y... cmo se aplica la aritmtica de punteros para
desplazarnos en un array bidimensional?:
float mat[2][4]; //declaracin del array
1.45 -23.5 -14,08 17.3
20 2.95 0.082 6.023
Fila 0
Fila 1
Col 0 Col 1 Col 2 Col 3
Utilizando punteros, la declaracin ser:

float (*mat)[4]; //array bidimensional
En donde mat es un puntero a un grupo contiguo de arrays
monodimensionales (vectores) de 4 elementos cada uno.

Existe, por tanto una equivalencia:
Con subndices Con punteros Valor
mat[0[[0] *(*(mat+0)+0) 1.45
mat[0[[1] *(*(mat+0)+1) -23.5
mat[0[[2] *(*(mat+0)+2) -14.08
mat[0[[3] *(*(mat+0)+3) 17.3
mat[1[[0] *(*(mat+1)+0) 20
mat[1[[2] *(*(mat+1)+1) 2.95
mat[1[[3] *(*(mat+1)+2) 0.082
mat[1[[4] *(*(mat+1)+3) 6.023
Recordemos que *mat representa un puntero a la primera
fila. A la segunda fila nos referimos mediante *(mat+1)+j para
las direcciones y con *(*(mat+1)+j) para los contenidos. El
segundo subndice actua sobre la columna.
30
31
Si en x[10][20] quiero acceder al elemento de la fila 3 y
la columna 6, lo hago escribiendo x[2][5]. Con notacin de
punteros, es equivalente a
* ( * ( x + 2 ) +5)
ya que x + 2 es un puntero a la fila 3. Por tanto. El
contenido de dicho puntero, *(x+2), es la fila 3. Si me
desplazo 5 posiciones en esa fila llego a la posicin
*(x+2)+5, cuyo contenido es *(*(x+2)+5). Ver dibujo:
32
Si en x[10][20] quiero acceder al elemento de la fila 3 y
la columna 6, lo hago escribiendo x[2][5]. Con notacin de
punteros, lo que hacemos es considerar que es un array
formado por 10 arrays unidimensionales (vectores) de 20
elementos cada uno, de modo que accedo a x[2][5] mediante
la expresin:
* ( * ( x + 2 ) +5)

ya que x + 2 es un puntero a la fila 3. Por tanto. El
contenido de dicho puntero, *(x+2), es la fila 3. Si me
desplazo 5 posiciones en esa fila llego a la posicin
*(x+2)+5, cuyo contenido es *(*(x+2)+5).

Si en int array[filas][columnas];
quiero acceder al elemento array[y][z] para asignarle un valor,
lo que el compilador hace es:
*(*array +columnas x y + z)) = 129; //asignacin
Si fuera int array[2][5] y quisiera asignar 129 al elemento de
la fila 1 y columna 2, pondra:
*(array + 5x1 + 1)) = 129;
es decir, desde el origen del array avanza 6 posicionesde
memoria: array[1][1]
fila 0 *(*(array+5)+1)
fila 1 129 *(*array + 6)


33
34
Punteros a CADENAS DE CARACTERES:
Una cadena de caracteres es un array de caracteres. La
forma de definir un puntero a una cadena de caracteres:
char *cadena;
El identificador del array es la direccin de comienzo del
array. Para saber dnde termina la cadena, el compilador
aade el carcter \0 (ASCII 0, NULL):
char *nombre = PEPE PEREZ;
P E P E P E R E Z \0
nombre
*(nombre+2)
direccin de memoria
contenido
35
P E P E P E R E Z \0
nombre
*(nombre+2)
Si quiero recorrer la cadena con notacin de puntero:
i = 0;
do
printf(%c, *(nombre+i);
while(*(nombre+ i ++)); //postincremento
Condicin de
salida
36
ARRAYS DE PUNTEROS A CADENAS DE CARACTERES:
En un array de punteros a cadenas de caracteres cada
elemento apunta a un carcter. La declaracin ser:
char *cad[10]; //por ejemplo
Grficamente podra ser:
cad
cad+1
. . .
H O L A \0
A D I O S \0
. . .
. . .
cad[0]
cad[4]
. . .
37
ARRAYS DE PUNTEROS A CADENAS DE CARACTERES:
La declaracin:
char cad[10][80];
Reserva memoria para 10 cadenas de caracteres de80
caracteres cada una. Pero en la declaracin como array de
punteros:
char *cad[10];
el compilador desconoce el tamao de las cadenas: cunta
memoria reserva?
- si el array es static y se inicializan las cadenas en el propio
cdigo, el compilador calcula la dimensin no explicitada
(arrays sin dimensin explcita).
38
ARRAYS DE PUNTEROS A CADENAS DE CARACTERES:
- si las dimensiones no son conocidas, sabremos dnde
comienzan las cadenas, pero no dnde terminan. Para ello se
efecta la llamada reserva dinmica de memoria
(operadores new y delete):
En char cad[10][80];
Equivale a char **cad reservando 800 bytes

... ... ... ...
inicio
reserva
...
39
PUNTEROS Y FUNCIONES :

Cuando C pasa argumentos a funciones, los pasa por valor, es
decir, si el parmetro es modificado dentro de la funcin, una
vez que termina la funcin el valor pasado de la variable
permanece inalterado.

Hay muchos casos que se quiere alterar el argumento pasado
a la funcin y recibir el nuevo valor una vez que la funcin ha
terminado. Para hacer lo anterior se debe usar una llamada
por referencia, en C se puede simular pasando un puntero al
argumento. Con esto se provoca que la computadora pase la
direccin del argumento a la funcin.


40
PUNTEROS Y FUNCIONES :
Para entender mejor lo anterior consideremos la funcin
swap() que intercambia el valor de dos argumentos enteros:

void swap(int *px, int *py);
main()
{
int x, y;
x = 10;
y = 20;
printf("x=%d\ty=%d\n",x,y);
swap(&x, &y); // Pasa la referencia o direccin de x y de y
printf("x=%d\ty=%d\n",x,y);
}
41
PUNTEROS Y FUNCIONES :
El desarrollo de la funcin swap() quedara de la siguiente
manera:

void swap(int *px, int *py) // recibe punteros a las variables
{
int temp;
temp = *px; /* guarda el valor de la direccin x */
*px = *py; /* pone y en x */
*py = temp; /* pone x en y */
}

Los contenidos de las variables x y de y son actualizados una
vez que la funcin se ejecute.



42
MEMORIA DINMICA:
Cuando se ejecuta un programa, el sistema operativo
reserva una zona de memoria para el cdigo o
instrucciones del programa y otra para los objetos que
se usan durante la ejecucin. A menudo estas zonas son
la misma, y componen lo que se denomina memoria local.
Tambin hay otras zonas de memoria, como la pila, que
se usa, entre otras cosas, para intercambiar datos
entre las funciones. El resto, la memoria que no se usa
por ningn programa es lo que se conoce como
almacenamiento libre o free store. Nuestro programa
puede hacer uso de esa memoria durante la ejecucin,
por eso se le denomina a este tipo memoria dinmica.
43
MEMORIA DINMICA:
C++ dispone de dos operadores para manejar (reservar y
liberar) la memoria dinmica, son new y delete. En C estas
acciones se realizan mediante funciones de la biblioteca
estndar stdio.
Hay una regla de oro cuando se usa memoria dinmica: toda
la memoria que se reserve durante el programa hay que
liberarla antes de salir del programa. No seguir esta regla
es una actitud muy irresponsable, y en la mayor parte de los
casos tiene consecuencias desastrosas. No os fiis de lo que
diga el compilador, de que estas variables se liberan solas al
terminar el programa, no siempre es verdad.


44

MEMORIA DINAMICA

Ejemplo:
int main()
{ int *a;
a = new int; // variable dinmica
*a = 10;
a = new int; // nueva variable dinmica,
// se pierde el puntero a la anterior
*a = 20;
delete a; // slo liberamos la ltima reservada
return 0;}

En este ejemplo se observa como es imposible liberar la
primera reserva dado que su direccin se perdi.
45
ARREGLOS DINAMICOS
Cuando se desconoce las dimensiones de un arreglo, lo mas
conveniente es hacer la reserva de memoria en forma
dinmica para el arreglo. Usando para ello el operador new y
delete , cuya sintaxis sera la siguiente:
T *Ptr; // Declaracin de puntero a tipo T
Ptr= new T [ N] // Reserva de memoria para N elementos
delete [] Ptr; // liberacin de todo el bloque

Es posible que no exista suficiente memoria disponible, en tal
caso el operador new nos devolver un puntero a NULL
46
ARREGLOS DINAMICOS
En un ejemplo completo de utilizacin de los operadores
new y delete . Segmento de cdigo que almacena valores
aleatorios entre 0 y 100 en un arreglo dinmico.
int*datos;//puntero a int
cout <<"Cuantos nmeros deseas generar:";
cin >> Num;
datos =new int[Num]; //Reserva de memoria para el arreglo
if(datos != NULL)
{for(inti=0;i<Num;i++)
{ datos[i] = rand() * 100 / RAND_MAX;
cout << i <<":"<< datos[i] <<endl;}
delete [] datos; //libera todo el bloque de memoria
}