Documentos de Académico
Documentos de Profesional
Documentos de Cultura
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
...
...
...
...
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:
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.
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
.printf(\n%f, altura);
.printf(\n%f, *apunta);
//salida 26.92
Con el operador
.
*p = 27;
en las instrucciones:
8
Veamos con un ejemplo en C la diferencia entre todos estos conceptos Veamos el archivo - punt0.cpp - punt1.cpp Ver sumpun.cpp
Es decir:
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
void main(void) {
int a, b, c, *p1, *p2; void *p;
p1 = &a;
*p1 = 1; p2 = &b; *p2 = 2; p1 = p2; *p1 = 0;
p2 = &c; *p2 = 3;
*p = p2;
*p1 = 1;
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];
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
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
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);
printf("\n%d", *(mat+i)); //resultado: 16 (valor de mat[1] o valor getch(); } //en la direccin 1502
Ismael Camarero 20
ejemplo
Parece deducirse que accedemos a los elementos del array de dos formas: - mediante el subndice. - mediante su direccin de memoria.
16
-4
29
234
12
3
21
Ismael Camarero
Analizando las direcciones de memoria del array: Direccin del elemento 0 Direccin del octavo elemento
2
mat
16
-4
29
234
12
3
mat+7
mat+1 mat+2
Incremento en una
mat++
22
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
-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 (III): -Observar las siguientes instrucciones: int *p; double *q;
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+7) = 3
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
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
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:
(*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
int x[10][20];
float p[10][20][30];
int *x[10];
int *p[10][20];
Ismael Camarero
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)
i = 0;
do printf(%c, *(nombre+i);
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:
...
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
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 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:
2. un puntero a
3. una funcin que devuelve 4. un puntero a 6. punteros a 7. objetos de tipo char.
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