Está en la página 1de 52

APUNTADORES EN C

Qu es un apuntador?:
Un apuntador es un objeto que apunta a otro objeto. Es decir, una variable cuyo valor es la direccin de memoria de otra variable. Las direcciones de memoria dependen de la arquitectura de la computadora y de la gestin que el sistema operativo haga de ella.

Qu es un apuntador?:
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

Declaracin de variables apuntador


Se declara como todas las variables y donde su identificador va precedido de un asterisco (*):

int *punt;
punt Es una variable apuntador a una variable de tipo entero. Un apuntador tiene su car Es un apuntador a variable de tipo carcter. propia direccin de long float *num; memoria: float *mat[5]; // . . . &punt &car
4

char *car;

Declaracin de variables apuntador


Es decir: hay tantos tipos de apuntadores como tipos de datos, aunque tambin pueden declararse apuntadores a estructuras ms complejas (funciones, struct, ficheros...) e incluso apuntadores vacos (void ) y apuntadores nulos (NULL). Ej.- Declaracin de variables apuntador: char dato; //variable que almacenar un carcter. char *punt; //declaracin de apuntador a carcter.

flotat *x;

//declaracin de apuntador a real

Personas *y; //declaracin de apuntador a estructura FILE *z; //declaracin de apuntador a archivo
5

Operadores de apuntadores
Existen dos operadores : 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: * Permite acceder a la variable situada en la direccin de memoria que se especifica en el operando. *punt es el contenido del la direccion de punt
6

Operadores de apuntadores
Ejemplo de operadores: float altura = 26.92, *apunta; apunta = &altura; //inicializacin del apuntador printf(\n%f, altura); //salida 26.92 printf(\n%f, *apunta); //salida 26.92 No se debe confundir el operador * en la declaracin del apuntador: Con el operador int *p;

*p = 27;

en las instrucciones:

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


7

Operadores de apuntadores
Veamos con un ejemplo en C la diferencia entre todos estos conceptos Es decir: int x = 25, *pint; pint = &x; pint apunta a la direccin de la variable x.

*pint es el valor de la variable (x), es decir 25.


pint tiene su propia direccin: &print

Operadores de apuntadores
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
9

Operadores de apuntadores
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);

10

main() { int j; int k; int l; int *pt1; // Declaramos un apuntador entero int *pt2; // Declaramos otro apuntador entero float values[100]; float results[100]; float *pt3; // Aqu se declara un apuntador flotante float *pt4; // Aqui se declaramos otro apuntador flotante j = 1; k = 2; pt1 = &j; // pt1 contiene la direccin de la variable j pt2 = &k; // pt2 contiene la direccin de la variable k pt3 = values; // pt3 contiene la direccin del primer elemento de values pt3 = &values[0]; // Esto es equivalente a la afirmacin de arriba return 0; }

11

Inicializacin de apuntadores(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 apuntador nulo a cualquier tipo de dato: int *p; p = NULL; //actualizacin
12

Inicializacin de apuntadores(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 apuntador a apuntador:


int *punt = (int *) 123.456; Inicializa el apuntador con el entero.
13

Inicializacin de apuntadores(III): 4) Un apuntador 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 apuntadores) static int x; int *punt = &x+2, *p = &x-1; 6) Equivalencia: Dos tipos definidos como apuntadores a objeto P y apuntador a objeto Q son equivalentes slo 14 si P y Q son del mismo tipo. Aplicado a matrices:

apuntadores 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
15

apuntadores Y ARRAYS
apuntadores 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]


16

apuntadores Y ARRAYS
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:
17

#include <stdio.h>

apuntadores Y ARRAYS

#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
18

apuntadores Y ARRAYS
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
19

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

apuntador a la direccin del elemento

Incremento en una

mat++
20

unidad int (dos bytes)

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 apuntadores
21

apuntadores Y ARRAYS

apuntadores Y ARRAYS

22

Aritmtica de apuntadores
-A una variable apuntador se le puede asignar la direccin de cualquier objeto. -A una variable apuntador se le puede asignar la direccin de otra variable apuntador (siempre que las dos sealen el mismo objeto)

-A un apuntador se le puede inicializar con el valor NULL


-Una variable apuntador puede ser restada o comparada con otra si ambas apuntan a elementos de un mismo array.

23

Aritmtica de apuntadores
Ejemplo: int *punt = NULL, var = 14;

punt = &var;
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).
24

Aritmtica de apuntadores
- 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 //p apunta a la siguiente direccin pv+=3 // pv apunta 3*n bytes del dato apuntado (offset)

25

-Observar las siguientes instrucciones: int *p; double *q; void *r; //apuntador genrico

Aritmtica de apuntadores

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
26

p = (int *)q;

Utilizando la aritmtica de apuntadores nos desplazamos de unas posiciones de memoria a otras. Pero. cmo acceder a los contenidos de esas posiciones utilizando notacin de apuntadores? mat[0] = 2 mat[7] = 3
mat[0] mat[1] mat[2] mat[3] mat[4] mat[5] mat[6] mat[7]

apuntadores Y ARRAYS

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 27 el contenido de la direccin de memoria apuntada.

apuntadores Y ARRAYS
Y... cmo se aplica la aritmtica de apuntadores 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 apuntadores, la declaracin ser:

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

apuntadores Y ARRAYS
Existe, por tanto una equivalencia: Con subndices
mat[0[[0]

Con apuntadores
*(*(mat+0)+0)

Valor
1.45

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

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

-23.5
-14.08 17.3

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

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

20
2.95 0.082 6.023

apuntadores Y ARRAYS
*mat representa un apuntador a la primera fila.
*(mat+1)+j para las direcciones de elementos *(*(mat+1)+j) para los contenidos.

30

apuntadores Y ARRAYS
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 apuntadores, es equivalente a
* ( * ( x + 2 ) +5) ya que x + 2 es un apuntador a la fila 3. Por tanto. El contenido de dicho apuntador, *(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).

31

apuntadores Y ARRAYS
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 apuntadores, 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 apuntador a la fila 3. Por tanto. El contenido de dicho apuntador, *(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 apuntadores son vlidas:
**x *(*x+1) x[0][0] ; x[0][1]; *(*(x+1)) **(x+1) x[1][0] x[1][0]
32

apuntadores Y ARRAYS
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 * 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

apuntadores Y ARRAYS
Un array multidimensional es, en realidad, una coleccin de vectores. Segn esto, podemos definir un array bidimensional como un apuntador 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]; apuntador a un grupo de arrays Ver pmatcon.cpp int (*dat)[col]

34

apuntadores Y ARRAYS
El array: int valor[x][y][z]; int (*valor)[y][z]; apuntador a un grupo de arrays bidimensionales Sea el array valor[2][2][3]: (*valor)[y][z] Puede ser representado en la forma: Ver: ardepun.cpp

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

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

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

la ltima de sus dimensiones.


Veamos ms declaraciones de arrays de apuntadores:

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

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

Ver ejemp11.cpp ejemp12.cpp


36

apuntadores a CADENAS
Una cadena de caracteres es un array de caracteres. La forma de definir un apuntador 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

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

Si quiero recorrer la cadena con notacin de apuntador:

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

Ver: pcad.cpp arraycad.cpp

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

Condicin de salida
38

En un array de apuntadores a cadenas de caracteres cada elemento apunta a un carcter. La declaracin ser: char *cad[10]; //por ejemplo

Arrays de apuntadores a CADENAS

Grficamente podra ser:


cad[0]

...
cad cad+1

H O L

A \0

...

cad[4]

A D I
...

O S

\0

...
39

Arrays de apuntadores a CADENAS


La declaracin: char cad[10][80]; Reserva memoria para 10 cadenas de caracteres de80 caracteres cada una. Pero en la declaracin como array de apuntadores:

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
40

Arrays de apuntadores a CADENAS


- 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

...

... ...

... ...
41

apuntadores A FUNCIONES:
Un apuntador a funcin es una variable cuyos posibles valores son direcciones en las que se encuentran funciones. La sintaxis de su definicin es la siguiente:
estilo nuevo: tipo (*nombre)(tipo,... ,tipo) estilo antiguo:
tipo (*nombre)( );

42

apuntadores A FUNCIONES:
La direccin de una funcin se obtiene con el nombre de la funcin sin parntesis ni argumentos (no hace falta el operador &): pf = nombre_funcin; Si el apuntador no apunta a ninguna funcin se inicializa a NULL: pf = NULL; Se puede realizar de dos maneras la invocacin de funciones mediante apuntadores : (*pf)( lista_parametros_actuales ); pf( lista_parametros_actuales ); Los apuntadores a funciones permiten pasar funciones como parmetros en la llamada a otras funciones. 43

apuntadores GENERICOS: Son tipo void: void *generico;

Los apuntadores 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
apuntadores NULO: En C un apuntador 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;
44

apuntadores A CONSTANTES: Una declaracin de apuntador precedida de const hace que el objeto apuntado sea una constante (aunque no el apuntador):
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 apuntador constante;


char *const p = Medina; p[0] = s; //error: el objeto Medina, es cte. p = Peafiel; //error: el apuntador p es constante.
45

apuntadores A apuntadores: Ver ejemp4.cpp, ejemp11.cpp y ejemp12.cpp int.

int **apuntador; //apuntador a apuntador a un objeto

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.
46

apuntadores A DATOS COMPLEJOS: Se pueden declara apuntadores a datos definidos por el usuario (typedef()), a datos struct, a funciones, como argumentos de funciones

47

DECLARACIONES COMPLEJAS:
Uno de los ms importantes defectos del lenguaje es la dificultad de la comprensin de las declaraciones donde intervienen simultneamente apuntadores, funciones y arrays La regla general para interpretar una declaracin compleja compuesta por funciones y apuntadores se basa en la precedencia de los operadores *, () y []. () y [] mayor precedencia, asociatividad ID * menor precedencia, asociatividad DI 48

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]


49

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 apuntador a
3. una funcin que devuelve 4. un apuntador a 6. apuntadores a 7. objetos de tipo char.

5. un array de 10 elementos, los cuales son


50

DECLARACIONES COMPLEJAS
Para ver declaraciones de mayor complejidad, y su significado, ver el documento de word:

declarpunt.doc

51

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
52