Transcript punteros

Ismael Camarero 1

¿Qué es un PUNTERO?:

Un puntero es un objeto que apunta a otro objeto. Es decir, una variable cuyo valor es la dirección de memoria de otra variable.

Las direcciones de memoria dependen de la arquitectura del ordenador y de la gestión que el sistema operativo haga de ella.

Ismael Camarero 2

¿Qué es un PUNTERO?:

No hay que confundir una dirección de memoria con el contenido de esa dirección de memoria.

int x = 25; Dirección 1502 1504 1506 1508

...

...

25 ...

...

...

...

La dirección de la variable x (&x) es 1502 El contenido de la variable x es 25 Ismael Camarero 3

Declaración de variables puntero

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

int *punt;

punt Es una variable puntero a una variable de tipo entero.

char *car;

car Es un puntero a variable de tipo carácter.

long float *num; float *mat[5]; // . . .

Un puntero tiene su

propia dirección

de memoria: &punt Ismael Camarero &car 4

Declaración de variables puntero

Es decir: hay tantos tipos de punteros como tipos de datos, aunque también pueden declararse punteros a estructuras más complejas (funciones, struct, ficheros...) e incluso punteros vacíos (void ) y punteros nulos (NULL).

Ej.- Declaración de variables puntero: char dato; //variable que almacenará un carácter.

char *punt; //declaración de puntero a carácter.

flotat *x; //declaración de puntero a real Personas *y; //declaración de puntero a estructura FILE *z; //declaración de puntero a archivo Ismael Camarero 5

Operadores de Punteros

Existen dos operadores :  Operador de dirección :

&

Representa la dirección de memoria de la variable que le sigue: &fnum representa la dirección de fnum.

 Operador de contenido o

indirección

: memoria que se especifica en el operando.

*

Permite acceder a la variable situada en la dirección de *punt es el contenido del la direccion de punt Ismael Camarero 6

Operadores de Punteros

Ejemplo de operadores: float altura = 26.92, *apunta; apunta = &altura; //inicialización del puntero printf(“\n%f”, altura); //salida 26.92

printf(“\n%f”, *apunta); //salida 26.92

No se debe confundir el operador * del puntero: int * p; en la declaración Con el operador . * en las instrucciones: * p = 27; printf(“\nContenido = %d”, *p); 7

Operadores de Punteros

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

Es decir: int x = 25, * pint; pint = &x; pint apunta a la dirección de la variable x.

*pint es el valor de la variable (x), es decir 25. pint tiene su propia dirección: &pint Ismael Camarero 8

Operadores de Punteros

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 dirección 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 dirección 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 9

Operadores de Punteros

p2 = &c; // Paso 7. La dirección 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 dirección 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? } Ismael Camarero 10

Inicialización de punteros(I): < Almacenamiento > < Tipo > * < Nombre > = < Expresión > Si es expresado.

extern o static , deberá ser una expresión constante del tipo Si es auto, entonces puede ser cualquier expresión del especificado.

Ejemplos: 1) La constante entera 0, NULL (cero) proporciona un puntero nulo a cualquier tipo de dato: int *p; p = NULL; //actualización 11

Inicialización de punteros(II): 2) El nombre de un array de almacenamiento static o extern se transforma según la expresión: 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 12

Inicialización de punteros(III): 4) Un puntero a carácter 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: (aritmética 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 sólo si P y Q son del mismo tipo.

Aplicado a matrices: 13 nombre_puntero = nombre_matriz;

PUNTEROS Y ARRAYS

Sea el array de una dimensión: 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 dirección de memoria del primer elemento, es 1500: &mat[0] es 1500 &mat[1] será 1502 &mat[7] será 1514 Ismael Camarero 14

PUNTEROS Y ARRAYS

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: Dirección 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] Ismael Camarero 15

PUNTEROS Y ARRAYS

Dirección 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] 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 posición y por dirección: ¿Es lo mismo &mat[0] y mat?

Y &mat[1] = mat++ ?

Veamos el código de un ejemplo: Ismael Camarero 16

PUNTEROS Y ARRAYS

#include #include int mat[5]={2, 16, -4, 29, 234, 12, 0, 3}, i; //declaradas como globales void main() { printf("\n%d", &mat[0]); //resultado: 1500 (dirección de mem) printf("\n%p", mat); i++; //resultado: 1500 ( " " " " " ) //i=1 printf("\n%p", mat+i); printf("\n%d", *(mat+i)); getch(); } //resultado: 1502 ( " " " " " ) //resultado: 16 (valor de mat[1] o valor //en la dirección 1502 Ismael Camarero 17

PUNTEROS Y ARRAYS

Comprobamos con un ejemplo: dirmen.cpp

ejemplo Parece deducirse que accedemos a los elementos del array de dos formas: - mediante el subíndice.

- mediante su dirección de memoria. Elemento mat[1] mat[2] mat[3] mat[4] mat[5] mat[6] mat[7]

2 16 -4 29 234 12 0 3

Ismael Camarero 18

Analizando las

direcciones de memoria

del array:

Dirección del elemento 0 Dirección del octavo elemento

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

2

mat

16

mat+1

-4

mat+2

29

mat+3

234 12

mat+4 mat+5

0

mat+6

3

mat+7

Puntero a la dirección del elemento 0 Incremento en una

Ismael Camarero

unidad int (dos bytes)

mat++

19

PUNTEROS Y ARRAYS

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 código:

for(i=0; i<8; i++) printf(“mat + %d = %p”, i, mat + = i);

A esta forma de desplazarse en memoria se le llama

Aritmética de punteros

Ismael Camarero 20

PUNTEROS Y ARRAYS

Ismael Camarero 21

Aritmética de punteros

-A una variable puntero se le puede asignar la dirección de cualquier objeto.

-A una variable puntero se le puede asignar la dirección de otra variable puntero (siempre que las dos señalen 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 22

Aritmética de punteros

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 dirección de memoria aumentada en una posición (int=2bytes), que será un valor no deseado. Sin embargo var+1 representa el valor 15.

punt + 1 representa lo mismo que &var + 1 dirección de memoria de var).

(avance en la 23

Aritmética de punteros

- 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 dirección pv+=3 // pv apunta 3*nº bytes del dato apuntado (offset) Si tenemos: float *decimal; // suponemos que apunta a 0000 decimal++; // apunta a 0004 Ismael Camarero 24

Aritmética de punteros

-Observar las siguientes instrucciones: int *p; double *q; void *r; //puntero genérico p = &34; // las constantes no tienen dirección p = &(i+1); // las expresiones no tienen dirección &i = p; // las direcciones no se pueden cambiar p = q; // ilegal Ismael Camarero p = (int *)q; // legal 25

PUNTEROS Y ARRAYS

Utilizando la punteros?

aritmética de punteros

a los contenidos nos desplazamos de unas posiciones de memoria a otras. Pero. ¿ cómo acceder de esas posiciones utilizando notación de

mat[0] = 2 mat[7] = 3

mat[0]

2

mat[1]

16

mat[2]

-4

mat[3]

29

mat[4]

234

mat [5]

12

mat[6]

0

mat[7]

3

* mat *(mat+1) *(mat+2) * ( mat+4) * ( mat+6) * (mat+3) * (mat+5)

*mat = 2 *(mat+7) = 3

Empleamos el operador

*

,

indirección

que nos da el contenido de la dirección de memoria apuntada.

26

PUNTEROS Y ARRAYS

Y... ¿cómo se aplica la aritmética de punteros desplazarnos en un array bidimensional?: para float mat[2][4]; //declaración del array

Col 0 Col 1 Col 2 Col 3 Fila 0

1.45

-23.5

-14,08 17.3

Fila 1

20 2.95

0.082

6.023

Utilizando punteros, la declaración será: En donde float (*mat)[4]; //array bidimensional mat es un puntero a un grupo contiguo de arrays monodimensionales (vectores) de 4 elementos cada uno. Ismael Camarero 27

PUNTEROS Y ARRAYS

Existe, por tanto una equivalencia:

Con subíndices Con punteros

mat[0[[0] *(*(mat+0)+0) mat[0[[1] mat[0[[2] *(*(mat+0)+1) *(*(mat+0)+2) mat[0[[3] mat[1[[0] mat[1[[2] mat[1[[3] mat[1[[4] *(*(mat+0)+3) *(*(mat+1)+0) *(*(mat+1)+1) *(*(mat+1)+2) *(*(mat+1)+3)

Valor

1.45

-23.5

-14.08

17.3

20 2.95

0.082

6.023

PUNTEROS Y ARRAYS

*mat representa un

puntero a la primera fila.

*(mat+1)+j para las direcciones de elementos *(*(mat+1)+j) para los contenidos.

Ismael Camarero 29

PUNTEROS Y ARRAYS

Si en

x[10][20]

quiero acceder al elemento de la fila 3 y la columna 6, lo hago escribiendo punteros, es equivalente a x[2][5] . Con notación de * ( * ( 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 posición *(x+2)+5 , cuyo contenido es *(*(x+2)+5). Ismael Camarero 30

PUNTEROS Y ARRAYS

Si en

x[10][20]

la columna 6, lo hago escribiendo 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 expresión: quiero acceder al elemento de la fila 3 y x[2][5] . Con notación de * ( * ( 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 posición *(x+2)+5 , cuyo contenido es *(*(x+2)+5). Las siguientes expresiones con punteros son válidas: **x x[0][0] ; *(*(x+1)) x[1][0] *(*x+1) x[0][1]; **(x+1) x[1][0] 31

PUNTEROS 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; //asignación Si fuera int array[2][5] y quisiera asignar 129 al elemento de la fila 1 y columna 2, pondría: *(array + 5x1 + 1)) = 129; es decir, desde el origen del array avanza

6

posiciones de memoria: array[1][1] fila 0

fila 1

    

129

  

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

PUNTEROS Y ARRAYS

Un array multidimensional es, en realidad, una colección de vectores . Según 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: int (*dat)[col] tipo_dato nombre[dim1][dim2]. . . . .[dimp]; equivale a: tipo_dato (*nombre)[dim2][dim3]. . . . .[dimp];

Puntero a un grupo de arrays

33

PUNTEROS Y ARRAYS

El array: int valor[x][y][z]; Puede ser representado en la forma: int ( *valor ) [y][z]; 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))[y][z] (*(valor+1)[1][1]

PUNTEROS Y ARRAYS

O como un ARRAY DE PUNTEROS : int *valor[x][y]; sin paréntesis En su nueva declaración desaparece la última de sus dimensiones.

Veamos más declaraciones de arrays de punteros: int x[10][20]; int *x[10]; float p[10][20][30]; int *p[10][20];

Array de 200 punteros , cada uno de los cuales apunta a un array de 30 elementos Ver ejemp11.cpp

ejemp12.cpp

Ismael Camarero 35

Punteros a CADENAS

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 dirección de comienzo del array. Para saber dónde termina la cadena, el compilador añade el carácter ‘\0’ (ASCII 0, NULL): char *nombre = “PEPE PEREZ”; nombre dirección de memoria *(nombre+2)

P E P E P E R E Z

\0 contenido 36

Punteros a CADENAS

nombre

P E P E P E R E Z

\0 *(nombre+2) Si quiero recorrer la cadena con notación de puntero: do i = 0; Ver: pcad.cpp

arraycad.cpp

printf(“%c”, *(nombre+i); while(*(nombre+ i ++)); //postincremento

Condición de salida

Ismael Camarero 37

Arrays de Punteros a CADENAS

En un array de punteros a cadenas de caracteres cada elemento apunta a un carácter. La declaración será: char *cad[10]; //por ejemplo Gráficamente podría ser: cad cad+1

. . .

cad[0]

H O L A \0

. . .

A D I

cad[4]

O S \0

. . .

Ismael Camarero . . .

38

Arrays de Punteros a CADENAS

La declaración: char cad[10][80]; Reserva memoria para 10 cadenas de caracteres de80 caracteres cada una. Pero en la declaración como array de punteros: char *cad[10]; el compilador desconoce el tamaño de las cadenas: ¿cuánta memoria reserva? - si el array es static y se inicializan las cadenas en el propio código, el compilador calcula la dimensión no explicitada (arrays sin dimensión explícita). Ver programa bornday.cpp

Ismael Camarero 39

Arrays de Punteros a CADENAS

- si las dimensiones no son conocidas, sabremos dónde comienzan las cadenas, pero no dónde terminan. Para ello se efectúa la llamada

reserva dinámica 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 ...

...

...

40 Ismael Camarero

PUNTEROS A FUNCIONES:

• Un puntero a función es una variable cuyos posibles valores son direcciones en las que se encuentran funciones.

• La sintaxis de su definición es la siguiente: – estilo nuevo:

tipo

(*

nombre

)(

tipo

,

...

,

tipo

)

– estilo antiguo:

tipo

(*

nombre

)( );

Ismael Camarero 41

PUNTEROS A FUNCIONES:

• La dirección de una función se obtiene con el nombre de la función sin paréntesis ni argumentos (no hace falta el operador

&

):

pf = nombre_función;

• Si el puntero no apunta a ninguna función se inicializa a NULL:

pf = NULL;

• Se puede realizar de dos maneras la invocación de funciones mediante punteros :

(*pf)(

lista_parametros_actuales

pf(

lista_parametros_actuales

)

;

);

Los punteros a funciones permiten pasar funciones como parámetros en la llamada a otras funciones.

42

PUNTEROS GENERICOS: Son tipo

void :

void *generico; Los punteros tipo void pueden apuntar a otro tipo de datos.

Es una operación delicada que depende del tipo de compilador. Es conveniente emplear el “casting” para la conversión. Aún así, no todas las conversiones están permitidas. Ver programa: castpunt.cpp

PUNTEROS NULO:

En C un puntero que apunte a un objeto válido nunca tendrá un valor cero. El valor cero se utiliza para indicar que ha ocurrido algún error (es decir, que alguna operación no se ha podido realizar) int *p = NULL; //int *p=0; 43

PUNTEROS A CONSTANTES:

precedida de Una declaración de puntero

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 = “Peñafiel”; //error: el puntero p es constante.

Ismael Camarero 44

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 después de una puede ser de cualquier clase.

doble indirección Permite manejar arrays de múltiples dimensiones con notaciones del tipo ***mat, de múltiple indirección que pueden generar problemas si el tratamiento no es el adecuado.

Ismael Camarero 45

PUNTEROS A DATOS COMPLEJOS:

Se pueden declara punteros a datos definidos por el usuario (typedef()), a datos struct, a funciones, como argumentos de funciones 46

DECLARACIONES COMPLEJAS:

• Uno de los más importantes defectos del lenguaje es la dificultad de la comprensión de las declaraciones donde intervienen simultáneamente punteros, funciones y arrays • La regla general para interpretar una declaración compleja compuesta por funciones y punteros se basa en la precedencia de los operadores

*

,

()

y

[]

.

()

y

[] *

mayor precedencia, asociatividad ID menor precedencia, asociatividad DI Ismael Camarero 47

DECLARACIONES COMPLEJAS

Para interpretar declaraciones complejas podemos seguir el orden: 1) Empezar con el identificador y ver si hacia la derecha hay corchetes o paréntesis.

2) Interpretar esos corchetes o paréntesis y ver si hacia la izquierda hay asteriscos.

3) Dentro de cada nivel de paréntesis, de más internos a más externos, aplicar puntos 1 y 2.

Veamos un ejemplo: char *(*(*var)( ))[10] Ismael Camarero 48

Aplicando los puntos anteriores, podemos decir que

char *

(

*

(

*var

) ( ) )

[10]

7 6 4 2 1 3 5

La interpretación es: 1.

La variable var es declarada como 2.

un puntero a 3.

una función que devuelve

Para declaraciones de mayor complejidad, ver el archivo “

declarpunt.doc” 4.

un puntero a 5 . un array de 10 elementos, los cuales son 6.

punteros a 7.

objetos de tipo char.

49

DECLARACIONES COMPLEJAS

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

Ismael Camarero 50

Febrero-2001

Todos los ejemplos están editados en el compilador Borland C++ 3.1

Comentarios, erratas... [email protected]

Deseo que te haya sido útil 51