Sei sulla pagina 1di 49

Ismael Camarero

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; Direccin 1502 1504 1506 1508

...

...

25

...

...

...

...

La direccin de la variable x (&x) es 1502 El contenido de la variable x es 25


Ismael Camarero 2

Las direcciones de memoria dependen de la arquitectura del ordenador 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.
Ismael Camarero 3

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. Un puntero tiene su char *car: propia Es un puntero a variable de tipo carcter. direccin de memoria: long float *num; &punt float *mat[5]; // . . . &car 4 Ismael Camarero

Es decir: hay tantos tipos de punteros como tipos de datos, aunque tambin pueden declararse punteros a estructuras ms complejas (funciones, struct, ficheros...) 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.
Ismael Camarero 5

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). 6

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
Ismael Camarero 7

float altura = 26.92, *apunta; apunta = &altura; //inicializacin del puntero

.printf(\n%f, altura);
.printf(\n%f, *apunta);

//salida 26.92

No se debe confundir el operador * en la declaracin del puntero: int *p;

Con el operador
.

*p = 27;

en las instrucciones:
8

printf(\nContenido = %d, *p);

Veamos con un ejemplo en C la diferencia entre todos estos conceptos Veamos el archivo - punt0.cpp - punt1.cpp Ver sumpun.cpp

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
Ismael Camarero 9

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;
*p1 = 1; p2 = &b; *p2 = 2; p1 = p2; *p1 = 0;

// Paso 1. La direccin de a es asignada a p1


// Paso 2. p1 (a) es igual a 1. Equivale a a = 1; // Paso 3. La direccin de b es asignada a p2 // Paso 4. p2 (b) es igual a 2. Equivale a b = 2; // Paso 5. El valor del p1 = p2 // Paso 6. b = 0
10

p2 = &c; *p2 = 3;

// Paso 7. La direccin de c es asignada a p2 // Paso 8. c = 3 // Paso 9. Qu se imprime?

printf("%d %d %d\n", a, b, c); p = &p1;

// Paso 10. p contiene la direccin de p1

*p = p2;
*p1 = 1;

// Paso 11. p1= p2;


// Paso 12. c = 1 // Paso 13. Qu se imprime?

printf("%d %d %d\n", a, b, c);

Ismael Camarero

11

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:

Ismael Camarero

12

Paso

a 0000

b 0002

c 0004

p1 0006
0000

p2 0008

p 0010

2
3 4 5

1
1 1 1 2 2

0000
0000 0000 0002 0002 0002 0000

6
7 8 9

1
1 1 1

0
0 0 0 3 3

0002
0002 0002 0002

0002
0004 0004 0004

10
11 12 13

1
1 1 1

0
0 0 0

3
3 1 1
Ismael Camarero

0002
0004 0004 0004

0004
0004 0004 0004

0006
0006 0006 0006
13

Inicializacin de punteros(I):
< Almacenamiento > < Tipo > * < Nombre > = < Expresin >

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
14

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.
Ismael Camarero 15

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) static 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;
16

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
Ismael Camarero 17

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

16

-4

29

234

12

Elemento mat[1] mat[2] mat[3] mat[4] mat[5] mat[6] mat[7]


Ismael Camarero 18

Direccin 1502

1504

1506

1508

1510

1512

1514

16

-4

29

234

12

Elemento mat[1] mat[2] mat[3] mat[4] mat[5] mat[6] mat[7] 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:
Ismael Camarero 19

#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);
i++; printf("\n%p", mat+i);

//resultado: 1500 ( " " " " " )


//i=1 //resultado: 1502 ( " " " " " )

printf("\n%d", *(mat+i)); //resultado: 16 (valor de mat[1] o valor getch(); } //en la direccin 1502
Ismael Camarero 20

Comprobamos con un ejemplo: dirmen.cpp

ejemplo

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

Elemento mat[1] mat[2] mat[3] mat[4] mat[5] mat[6] mat[7]

16

-4

29

234

12

3
21

Ismael Camarero

Analizando las direcciones de memoria del array: Direccin del elemento 0 Direccin del octavo elemento

&mat[0] &mat[1] &mat[2] &mat[3] &mat[4] &mat[5] & mat[6] &mat[7]

2
mat

16

-4

29

234

12

3
mat+7

mat+1 mat+2

mat+3 mat+4 mat+5 mat+6

Puntero a la direccin del elemento 0

Incremento en una

mat++
22

unidad int (dos bytes)

Ismael Camarero

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
Ismael Camarero 23

Ismael Camarero

24

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.
Ismael Camarero 25

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++; Si tenemos: float *decimal; //suponemos que apunta a 0000 decimal++; //apunta a 0004
Ismael Camarero 26

//p apunta a la siguiente direccin

pv+=3 // pv apunta 3*n bytes del dato apuntado (offset)

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 // legal
Ismael Camarero 27

p = (int *)q;

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? mat[0] = 2 mat[7] = 3
mat[0] mat[1] mat[2] mat[3] mat[4] mat[5] mat[6] mat[7]

2
*mat

16

-4

29
*(mat+3)

234
*(mat+4)

12
*(mat+5)

0
*(mat+6)

*(mat+1) *(mat+2) *mat = 2

*(mat+7) = 3

Empleamos el operador , indireccin que nos da 28 el contenido de la direccin de memoria apuntada.

Y... cmo se aplica la aritmtica de punteros para desplazarnos en un array bidimensional?: float mat[2][4]; Col 0 Fila 0 Col 1 Col 2 //declaracin del array Col 3

Fila 1

1.45 20

-23.5 2.95

-14,08 0.082

17.3 6.023

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.
Ismael Camarero 29

Existe, por tanto una equivalencia: Con subndices


mat[0[[0] mat[0[[1]

Con punteros
*(*(mat+0)+0) *(*(mat+0)+1)

Valor
1.45 -23.5

mat[0[[2]
mat[0[[3] mat[1[[0] mat[1[[2] mat[1[[3]

*(*(mat+0)+2)
*(*(mat+0)+3) *(*(mat+1)+0) *(*(mat+1)+1) *(*(mat+1)+2)

-14.08
17.3 20 2.95 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.

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:

Ismael Camarero

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, 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) Ver: ardepunt.cpp. pmatcon.cpp

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). Las siguientes expresiones con punteros son vlidas:
**x *(*x+1) x[0][0] ; x[0][1]; *(*(x+1)) **(x+1) x[1][0] x[1][0]
32

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 posiciones

de memoria:

array[1][1]

fila 0 fila 1

*(*(array+5)+1) *(*array + 6)

129

PUNTEROS A ARRAYS Un array multidimensional es, en realidad, una coleccin de vectores. Segn esto, podemos definir un array bidimensional como un puntero a un grupo contiguo de arrays unidimensionales. Las declaraciones siguientes son equivalentes: int dat[fil][col] En general: tipo_dato nombre[dim1][dim2]. . . . .[dimp]; equivale a: tipo_dato (*nombre)[dim2][dim3]. . . . .[dimp]; Puntero a un grupo de arrays
Ismael Camarero Ver pmatcon.cpp 34

int (*dat)[col]

El array:

int valor[x][y][z]; int (*valor)[y][z];

Puede ser representado en la forma: Ver: ardepun.cpp

Puntero a un grupo de arrays bidimensionales Sea el array valor[2][2][3]: (*valor)[y][z]

(*valor)[1][2]
(*(valor+1)[1][1]

(*(valor+1))[y][z]

O como un ARRAY DE PUNTEROS: int *valor[x][y]; sin parntesis En su nueva declaracin desaparece Array de 200 punteros, cada uno de los cuales apunta a un array de 30 elementos

la ltima de sus dimensiones.


Veamos ms declaraciones de arrays de punteros:

int x[10][20];
float p[10][20][30];

int *x[10];
int *p[10][20];
Ismael Camarero

Ver ejemp11.cpp ejemp12.cpp


36

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;

nombre

direccin de memoria

P E P E
*(nombre+2)

P E R E Z \0
contenido
37

nombre

P E P E

P E R E Z \0
*(nombre+2)

Si quiero recorrer la cadena con notacin de puntero:

i = 0;
do printf(%c, *(nombre+i);

Ver: pcad.cpp arraycad.cpp

while(*(nombre+ i ++)); //postincremento


Ismael Camarero

Condicin de salida
38

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[0]

...
cad cad+1

H O L

A \0

...

cad[4]

A D I
...
Ismael Camarero

O S

\0

...
39

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). Ver programa bornday.cpp
Ismael Camarero 40

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 (funciones malloc, calloc(), realloc() y free() de stdlib.h alloc.h): char cad[10][80];

Equivale a

char **cad reservando 800 bytes

Ver programas ardinam1.cpp y ardinam2.cpp

inicio

reserva

...

... ...
Ismael Camarero

... ...
41

OTRAS CLASES DE PUNTEROS: Punteros genricos: Son tipo void: void *generico; Los punteros tipo void pueden apuntar a otro tipo de datos. Es una operacin delicada que depende del tipo de compilador. Es conveniente emplear el casting para la conversin. An as, no todas las conversiones estn permitidas. Ver programa: castpunt.cpp Puntero nulo: En C un puntero que apunte a un objeto vlido nunca tendr un valor cero. El valor cero se utiliza para indicar que ha ocurrido algn error (es decir, que alguna operacin no se ha podido realizar) int *p = NULL; //int *p=0;
42

OTRAS CLASES DE PUNTEROS:


Punteros constantes: Una declaracin de puntero precedida de const hace que el objeto apuntado sea una constante (aunque no el puntero): const char *p = Valladolid; p[0] = f //error. La cadena apuntada por + es cte.

p = Pucela //Ok. p apunta a otra cadena.


Si lo que queremos es declarar un puntero constante; char *const p = Medina; p[0] = s; //error: el objeto Medina, es cte. p = Peafiel; //error: el puntero p es constante.
Ismael Camarero 43

OTRAS CLASES DE PUNTEROS: Punteros a punteros:

Ver ejemp4.cpp, ejemp11.cpp y ejemp12.cpp


int **puntero; //puntero a puntero a un objeto int. El tipo de objeto apuntado despus de una doble indireccin puede ser de cualquier clase. Permite manejar arrays de mltiples dimensiones con notaciones del tipo ***mat, de mltiple indireccin que pueden generar problemas si el tratamiento no es el adecuado. Ojo a los punteros locos.
Ismael Camarero 44

OTRAS CLASES DE PUNTEROS: Punteros a datos complejos: Se pueden declara punteros a datos definidos por el usuario (typedef()), a datos struct, a funciones, como argumentos de funciones... Declaraciones complejas: Una declaracin compleja es un identificador con ms de de un operador. Para interpretar estas declaraciones hace falta saber que los corchetes y parntesis (operadores a la derecha del identificador tienen prioridad sobre los asteriscos (operadores a la izquierda del identificador. Los parntesis y corchetes tienen la misma prioridad y se evalan de izquierda a derecha. A la izquierda del todo el tipo de dato.Empleando parntesis se puede cambiar el orden de prioridades. Las expresiones entre parntesis se evalan primero, de ms 45 internas a ms externas.

Declaraciones complejas: Para interpretar declaraciones complejas podemos seguir el orden: 1) Empezar con el identificador y ver si hacia la derecha hay corchetes o parntesis. 2) Interpretar esos corchetes o parntesis y ver si hacia la izquierda hay asteriscos. 3) Dentro de cada nivel de parntesis, de ms internos a ms externos, aplicar puntos 1 y 2.

Veamos un ejemplo:

char *(*(*var)( ))[10]


Ismael Camarero 46

Aplicando los puntos anteriores, podemos decir que

char *(*(*var)( ))[10]


7 6 4 2 1 3 5
Para declaraciones de mayor complejidad, ver el archivo declarpunt.doc La interpretacin es: 1. La variable var es declarada como

2. un puntero a
3. una funcin que devuelve 4. un puntero a 6. punteros a 7. objetos de tipo char.

5. un array de 10 elementos, los cuales son


47

Para ver declaraciones de mayor complejidad, y su significado, ver el documento de word: declarpunt.doc

Ismael Camarero

48

Febrero-2001
Todos los ejemplos estn editados en el compilador

Borland C++ 3.1


Comentarios, erratas... icamarero98@hotmail.com Deseo que te haya sido til
49

Potrebbero piacerti anche