Download punteros

Document related concepts
no text concepts found
Transcript
INGENIERIA INFORMATICA
Lenguajes de Programación
E.P.S.
PRÁCTICAS de Lenguaje C
Curso 2002-2003
Tema: Punteros
Lenguajes de Programación
Apuntadores
PUNTEROS
Definición de puntero
Un puntero es una variable que contiene una dirección de memoria. Normalmente esta dirección es la
posición de otra variable en memoria. En este caso se dice que la primera variable apunta a la segunda.
Dirección de memoria Contenido
1000 (variable num)
5
1000
1003 (variable p)
variable
p
num
*p
&p
&num
valor
1000
5
5
1003
1000
La posición de memoria 1000 contiene la variable num con el valor 5, la variable p es un puntero a la
variable num.
Declaración de punteros
La sintaxis es la siguiente:
tipo_elemento_apuntado
*nombre_de_la_variable_apuntadora;
Ejemplos:
char *pNombre;
/* Puntero a un carácter */
int
*pEdad;
/* Puntero a un entero */
float *pEstatura;
/* Puntero a un número real (float) */
struct dUsuario *usuario;
/* Puntero a una estructura */
void *pNada;
/* Puntero a un tipo sin definir */
Operadores y operaciones con punteros
Los operadores que podemos utilizar con los punteros son : *, &, ->
-
El operador * devuelve el contenido de la variable apuntada por el puntero
-
El operador & devuelve la dirección de memoria de la variable apuntada
-
El operador-> permite acceder a los campos de una estructura apuntada por un puntero
Y las operaciones que podemos usar son:
-
Asignación (=) : Asignar una dirección de memoria a un puntero.
-
Comparación (= =, !=): Comparación de punteros igualdad y diferencia.
-
Incremento (++), Decremento (--): Hace que el operador apunte al elemento siguiente o
anterior de una serie de elementos (por ejemplo, un array).
-
Indexado ( [] ): Acceder al elemento n-ésimo de una serie de elementos (por ejemplo, un
array).
Pràctiques
Pág. 2
Lenguajes de Programación
Apuntadores
Ejemplo 1:
#include <stdio.h>:
void main()
{
int n1, n2;
int *pn1, *pn2;
n1=3;
/* inicialización de las variables */
n2=10;
pn1=pn2=NULL;
pn1=&n1;
pn2=&n2;
/* ‘pn1’ apunta a ‘n1’ */
/* ‘pn2’ apunta a ‘n2’ */
if (pn1!=pn2)
{
printf(“’pn1’ y ‘pn2’ apuntan a diferentes posiciones de memoria
\n”);
printf(“El contenido de ‘pn1’ es: %d \n”, *pn1);
printf(“El contenido de ‘pn2’ es: %d \n”, *pn2);
}
pn2=pn1;
if (pn1==pn2)
{
printf(“’pn1’ y ‘pn2’ apuntan a la misma posición de memoria \n”);
printf(“El contenido de ‘pn1’ y ‘pn2’ es: %d \n”,*pn1);
}
}
Ejemplo 2:
#include <stdio.h>
typedef struct
{
char titulo[60];
char autor[40];
char editorial[30];
unsigned int precio;
} Libro;
void main()
{
Libro l;
Libro *pl;
strcpy(l.titulo, “La isla del tesoro”); /* inicialización de la
estructura */
strcpy(l.autor, “Robert Louise Stevenson”);
strcpy(l.editorial, “Planeta”);
l.precio=800;
pl=&l;
/* ‘pl’ apunta a ‘l’ */
/* acceso a la estructura por medio del puntero ‘pl’ */
printf(“Título: %s \n”, pl->titulo);
printf(“Autor: %s \n”, pl->autor);
printf(“Editorial: %s \n”, pl->editorial);
printf(“Precio: %d \n”, pl->precio);
}
Pràctiques
Pág. 3
Lenguajes de Programación
Apuntadores
Ejercicio 1:
Comprobar depurando el programa (debug) los distintos valores de las distintas variables que aparecen
en el programa anterior
Asignación dinámica de memoria
La asignación dinámica de memoria permite la creación de variables durante la ejecución del
programa. El espacio para almacenar las variables se obtiene de la memoria que este libre.
Las funciones que permiten la gestión dinámica de memoria son malloc() y free(). Cada vez que se
realiza una petición de memoria con malloc se reserva una cierta cantidad de memoria y cuando se
invoca la función free, la memoria se libera para poder ser reutilizable. Para poder usar estas funciones
es necesario usar el fichero de cabecera stdlib.h (#include <stdlib.h>).
La sintaxis de la función malloc es:
variable_puntero = malloc (tamagno_bloque_memoria);
La sintaxis de la función free:
free (variable_puntero);
Debido a que la función malloc puede reservar memoria para cualquier tipo de variables, la función
devuelve un puntero de tipo void, es decir, un puntero a un tipo sin determinar. De esta manera el valor
devuelto por malloc puede asignarse a cualquier tipo de puntero. Para conseguir el tipo de puntero
correcto habrá que efectuar una conversión de formato, esta operación también se conoce como
castting En la práctica se debe colocar el tipo de puntero deseado entre paréntesis delante de la palabra
malloc. Por ejemplo:
int *pn;
castting
···
pn=(int *)malloc(100); // Reserva memoria para 100 enteros.
···
free(pn);
Una variable de tipo char ocupa un byte de memoria, un entero 2 bytes etc. Pero... ¿Cuánto ocupa una
estructura de tipo Libro? La función sizeof devuelve el número de bytes que necesita cualquier tipo.
Por ejemplo:
int BytesInt,BytesLibro;
···
BytesInt=sizeof(int);
BytesLibro=sizeof(Libro);
···
En las variables BytesInt y BytesLibro hemos almacenado el tamaño de las variables de tipo int y
Libro respectivamente.
Por lo tanto para reservar memoria para N variables de un cierto tipo usaremos el código siguiente
Pràctiques
Pág. 4
Lenguajes de Programación
Tipo1 *PTipo1;
···
PTipo1=(Tipo1*)malloc(N*sizeof(Tipo1));

Apuntadores
//Reserva memoria para
//N variables de tipo Tipo1.
Se puede acceder a cada una de estas variables a través de un índice: Ptipo1[0] es la primera
variable, PTipo1[1] es la segunda,· · · · Ptipo1[N-1] es la última. Como siempre la primera
variable se indexa con 0 !!!!!!!
Ejercicio 2: Escribir un programa que pregunte al usuario un número entero L. Crearemos de forma
dinámica un vector de números enteros de longitud L y lo rellenaremos usando la fórmula L[i]=3*i.
No os olvidéis de liberar la memoria al acabar el programa!!! (Recordad que tenéis que adjunta el
código de todos los programas que escribáis o modifiquéis con la memoria de la práctica)
Relación de los punteros con los arrays y cadenas
Cuando utilizamos el nombre de un array o de una cadena, en realidad estamos utilizando un puntero al
primer elemento de este array. Si x es un array, la dirección del primer elemento se obtiene mediante la
expresión &(x[0]), o simplemente con x. De la misma manera, la dirección del segundo elemento de un
array se puede escribir como &(x[1]), o bien (x+1). En general la dirección de memoria en la que se
encuentra el elemento i+1 (teniendo en cuenta que el índice correspondiente al primer elemento de un
array es 0 i n-1) se puede expresar como &(x[i]), o bien como (x+i).
ejemplo:
char str[80],
…
pstr=str;
…
str[4]=’a’;
*(str+4)=’b’;
*pstr;
/* equivale a pstr=&str[0] */
/* acceso al quinto elemento de la cadena */
/* acceso al quinto elemento de la cadena */
Ejercicio 3:
Analizar la salida del siguiente programa:
#include <stdio.h>
void main()
{
int x[10]={10,11,12,13,14,15,16,17,18,19};
int i;
for (i=0; i<10; i++)
printf(“i= %d
x[i]= %d *(x+i)= %d &x[i]= %X
i, x[i], *(x+i), &x[i], x+i);
x+i= %X\n”,
}
Paso de parámetros por referencia a una función
Se pueden pasar parámetros a una función bien por valor o por referencia. Cuando se pasan los
parámetros por valor se realiza una copia de la variable (se crea una nueva variable) con la cual trabaja
la función. Por lo tanto, si este valor varía, la variación se pierde tan pronto como salimos de la función
llamada.
Pràctiques
Pág. 5
Lenguajes de Programación
Apuntadores
Cuando pasamos un valor por referencia se copia la posición de memoria en la cual se encuentra la
variable. Por lo tanto, cualquier operación que se realice dentro de la función llamada se conservará
cuando volvamos al programa principal.
Paso por referencia de tipos simples y estructuras
Para pasar por referencia una variable, cuando se llama a la función se ha de utilizar el operador &
como prefijo de la variable. En la declaración de la función, el parámetro correspondiente a de estar
declarado como un puntero. Finalmente, dentro de la función, podemos referirnos a la variable con el
operador *. Un ejemplo aclarará estos conceptos:
#include <stdio.h>
void ModificaValores(int*,int*);
void main()
{
int x,y;
x=22;
/* inicialización de las variables */
y=89;
printf(“Valor inicial de las variables x e y:\n”);
printf(“El valor de x es: %d
El valor de y es: %d\n”, x, y);
ModificaValores(&x,&y);
printf(“Valor de las variables x e y después de ModificaValores:\n”);
printf(“El valor de x es: %d
El valor de y es: %d\n”, x, y);
}
void ModificaValores(int *a,int *b)
{
*a=(*a)+1;
*b=(*b)+2;
}
Ejercicio 4: Escribe una función que permute dos números (ejercicio tres de la práctica anterior)
Ejercicio 5: Modificar el ejercicio 6 de la práctica anterior de forma que la variable CD sea LOCAL
y se pase a las funciones auxiliares por referencia.
Ejercicio 6: Definir una estructura (que llamaremos agenda) que permita almacenar un número de
teléfono y un nombre. Implementar una función que dado un array de agendas (de dimensión N) y un
nombre. Imprima por pantalla el nombre y su teléfono. El prototipo de la función debe ser:
void consulta(char *nombre, agenda *libreta, int N);
Nota: para comparar dos cadenas se puede usar la función "strcmp"
Ejercicio 7: Sumar (componente a componente) dos vectores de la misma longitud y almacenar el
resultado en un tercer vector. Los vectores deben reservarse de forma dinámica.
Pràctiques
Pág. 6