Subido por Oscar Olivar

Punteros a imprimir

Anuncio
M. C. Bertha López Azamar
Universidad del Papaloapan
Materia:
Programación Estructurada
Tema:
Punteros
Expositor:
M. C. Bertha López Azamar
Tuxtepec, Oaxaca.
Enero/2005-2006
1
M. C. Bertha López Azamar
PUNTEROS
Un tema muy ligado al uso de arreglos y paso de
parámetros por referencia a funciones, es el uso de
punteros.
Un puntero, es una variable que contiene la
dirección de memoria, de un dato o de otra
variable que contiene al dato. (El puntero
apunta al espacio físico donde está el dato de la
variable).
Puntero
El contenido es
una dirección
de memoria
3
1000
2
M. C. Bertha López Azamar
Antes de continuar es importante recordar aspectos
como los siguientes:
dato
memoria
3
Espacio de
almacenamiento
Toda variable tiene:
• nombre (identificador),
• tipo de dato, que permite reservar un espacio de
almacenamiento (dirección de memoria) y
• contenido .
3
M. C. Bertha López Azamar
El programador al realizar la declaración de la variable, reserva un
espacio de almacenamiento en memoria, conoce el nombre, pero el
Sistema Operativo (S.O.) es quien asigna la dirección de memoria.
Ejemplo:
Reserva 16 bits de memoria
Permite referenciar ese espacio
Tipo de dato
nombre
de memoria en el código
int
Edad;
Edad = 28;
//declaración de la variable
//asignación del valor
valor
El S.O. encuentra
una espacio libre de
memoria y la asocia
3
Edad
con el nombre de la
nombre
1000
variable.
contenido
Dirección de memoria
de la variable
4
M. C. Bertha López Azamar
Un puntero puede apuntar a un objeto de cualquier
tipo, incluyendo variables simples, arreglos,
estructuras, funciones, etc.
Puntero
*p
Puntero
*q
3
1000
‘C’
Por esa razón su contenido es una
dirección de memoria y no un valor
cualquiera.
5000
5
M. C. Bertha López Azamar
Las variables tienen nombre, contenido y
dirección de memoria.
*p
1000
200
edad
3
1000
*q
letra
‘C’
5000
5000
300
6
M. C. Bertha López Azamar
Al hablar de puntero, se distinguen dos
operadores:
- operador dirección de: &
el cual devuelve como resultado la
dirección de
su operando.
- operador de indirección: *
toma el contenido de su operando como
una dirección y nos da como resultado el
contenido en esa dirección.
7
NOTA:
M. C. Bertha López Azamar
Los operadores unarios * y & tienen mayor
precedencia que los operadores aritméticos +, –, *, /,
% e igual precendencia que ++ y --.
Mayor
( ) [ ] -> .
Menor
! ~ ++ -- - (tipo) * &
*/%
+<< >>
< <= > =>
== !=
&
^
|
&&
||
?:
= += -= *= /= etc.
,
sizeof
8
Declaración de un puntero:
M. C. Bertha López Azamar
Un puntero se declara precediendo el identificador
que referencia al puntero, por el operador de
indirección ( * ), el cual significa “puntero a”.
La declaración de un puntero tiene la siguiente
sintaxis:
tipo *var_puntero;
donde:
tipo.- especifica el tipo de objeto apuntador, puede
ser cualquier tipo, incluyendo tipos agregados.
var_puntero.- nombre de la variable.
9
Ejemplo: se declaran dos variables simples:
int edad;
char letra;
M. C. Bertha López Azamar
edad
letra
1000
5000
Al declarar los punteros para que apunten a estas
variables, estos deben ser del mismo tipo de dato que la
variable a la que apuntarán.
int *p;
char *q;
/*declara p como puntero a un entero*/
/*declara p como puntero a una cadena
de caracteres*/
*q
*p
200
300
10
M. C. Bertha López Azamar
Para asignar valores a variables simples, podemos emplear el
operador de asignación (=) o la lectura del valor mediante
una función de lectura por teclado (por ejemplo: scanf)
//asigna un valor a la variable
edad = 3;
letra = ‘C’;
edad
letra
3
1000
C
5000
Para asignar contenido a un PUNTERO, podemos emplear
el operador de asignación (=) en conjunto con el operador
de dirección (&). Pero NUNCA lea el contenido de una
variable puntero con una función de lectura por teclado (por
ejemplo: scanf)
//asigna la dirección de memoria de la variable al puntero
p = &edad;
q = &letra ;
*p
edad
1000
3
1000
200
*q
letra
5000
‘C’
5000
11
300
M. C. Bertha López Azamar
Reuniendo el código tenemos:
int edad, *p;
char letra, *q;
//Las asignaciones siguientes son correctas
edad = 3;
letra = ‘C’;
p = &edad; /*al puntero ‘p’ le asigna la dirección de memoria
de la variable edad. La cual es conocida por el
sistema operativo.*/
q = &letra;
//Las asignaciones siguientes son incorrectas:
p = 5; /*no puedo asignarle un valor cualquiera a un puntero,
solo direcciones de otra variable del mismo tipo*/
p = &letra; /*no puedo asignarle la dirección de una
variable de otro tipo de dato distinto al cual se declaro
el puntero.*/
12
M. C. Bertha López Azamar
Para obtener el valor del dato al cual apunta el puntero, se
emplea el operador de indirección (*) junto al nombre del
puntero. Si solo se pone el nombre del puntero, lo que se
obtiene es el contenido de la variable puntero, es decir, una
*p
dirección de memoria.
edad
1000
*p se puede leer como sigue:
200
3
1000
 toma el contenido del puntero ‘p’ (dirección 1000),
y dame el contenido que se encuentra en esa
dirección de memoria (3).
 dame el contenido que se encuentra en la dirección
de memoria que contiene la variable p.
 dame el contenido de la dirección de memoria a la
que apunta p.
13
M. C. Bertha López Azamar
Por lo tanto, para obtener el contenido de la *p
variable ‘edad’, es decir el 3, usando el
1000
puntero, debe escribirse *p, con lo que
200
podemos accesar indirectamente al contenido
que apunta el puntero ‘p’.
Al escribir el código:
edad = *p + 1;
*p
1000
200
edad
3
1000
edad
4
1000
Toma indirectamente el contenido de la dirección de
memoria a la que apunta ‘p’, es decir, el contenido de la
variable ‘edad’ y le suma un uno y luego lo asigna a la
variable edad (lo que hace es 3+1 4) . Es lo mismo que
escribir:
edad = edad + 1; si las variables ‘p’ y ‘edad’ se declararón
en la misma función.
14
El código:
M. C. Bertha López Azamar
*p = 1;
*p
1000
200
edad
1
1000
Lo que hace es asignar
indirectamente un 1 al
contenido de la dirección de
memoria a la que apunta ‘p’,
es decir, a la variable ‘edad’.
Si lo que se quiere es leer el valor de una variable
mediante un puntero, se debe recordar que al usar la
variable scanf, el segundo argumento que requiere es una
dirección de memoria de una variable, y que el puntero en
si, ya contiene una dirección de memoria de una variable.
15
Así el código:
M. C. Bertha López Azamar
scanf(“%d”, &edad);
El ampersand(&), permite obtener la dirección de
memoria de la variable edad. Al no ser puntero, el
colocar el ampersand esto es correcto.
scanf(“%d”, p);
En el caso de leer indirectamente mediante el
puntero, el uso del ampersand es incorrecto, ya
que el contenido del puntero en si es la dirección
de memoria donde se ubica la variable edad. Al
ser puntero, el colocar el ampersand esto sería
incorrecto.
16
M. C. Bertha López Azamar
Los punteros se pueden utilizar para:
•
Crear y manipular estructuras de datos,
•
Asignar memoria dinámicamente, y
• Proveer el paso de parámetros por
referencia en las llamadas a funciones.
17
Respecto al paso de parámetros:
M. C. Bertha López Azamar
Recordemos el ámbito de las variables:
• locales
• globales
Variables globales
Variables
locales
datos
datos
Paso
de
parámetros
Función1
datos
Variables
locales
Función2
datos
18
M. C. Bertha López Azamar
Respecto al paso de parámetros:
Básicamente puede realizarse el
paso de parámetros de dos
formas :
• Por valor
• Por referencia
19
M. C. Bertha López Azamar
El paso por valor, copia el valor de un argumento en el
parámetro formal de la subrutina. Los cambios
realizados sobre el parámetro no afectan al argumento.
{
}
A=5;
B=7;
Funcion( A, 18, B*3+4)
5
18
25
5
18
25
void Funcion(int x, int y, int z)
{
}
Argumentos
(parámetros actuales –
pueden ser variables,
constantes, o expresiones)
Copias de valores
parámetros formales
20
M. C. Bertha López Azamar
La llamada por referencia, copia la dirección del
argumento en el parámetro. Dentro de la subrutina se
usa la dirección para acceder realmente al argumento
usado en la llamada, con lo que los cambios sufridos por
el parámetro se reflejan en el argumento.
{
A=5;
B=7;
Funcion( &A, &B);
}
argumentos
(parámetros actuales
– deben ser
variables)
direcciones de memoria
void Funcion(int *x, int *y)
{
}
parámetros formales
(variables de tipo
puntero)
21
M. C. Bertha López Azamar
#include<stdio.h>
#include<conio.h>
void Funcion(int *, int *);
void main()
{
int A =5, B=7;
//muestra el contenido de A, y B, o sea 5 y 7
printf(“A = %d, B = %d”, A, B);
printf(“Introduce el valor de A: ”);
scanf(“%d”, &A); //lee un valor y lo guarda en A
Funcion( &A, &B);
//muestra el contenido de A, y B al regresar de la función
printf(“A = %d, B = %d”, A, B);
getch();
}
22
M. C. Bertha López Azamar
void Funcion(int *x, int *y)
{
/*asigna un valor en la dirección a la que apunta ‘y’,
es decir, en el contenido de la variable A.*/
*x = 20;
//muestra el contenido de A, y B, o sea 20 y 7
printf(“A = %d, B = %d”, *x, *y);
//lee el valor de ‘B’ através del puntero ‘y’
scanf(“%d”, y);
//muestra el contenido de A, y B,
printf(“A = %d, B = %d”, *x, *y;);
}
23
PUNTEROS y ARREGLOS
M. C. Bertha López Azamar
Tratándose de arreglos, debe recordarse que en si el
nombre del arreglo es en sí un puntero.
Al declarar un puntero lo que se hace es reservar un
espacio de memoria (según el tipo de dato especificado)
para un número determinado de datos y adicionalmente
se reserva espacio para una variable puntero, la cual es
la que tiene el nombre.
Entonces el puntero apunta al inicio del espacio de
memoria reservado (la primera posición del arreglo) y
permite referenciar las posiciones de memoria con ese
nombre y un índice que indica en si la posición, (y el
S.O. calculara con esto el desplazamiento en memoria.
24
M. C. Bertha López Azamar
Así tenemos el código de declaración de un arreglo:
int mes_d[31];
Gráficamente podemos representarlo de la siguiente forma:
mes_d
5000
4000
5000
25
M. C. Bertha López Azamar
En el caso del paso de parámetros de un arreglo, siempre
se realiza por referencia, tenemos que pasar una dirección
de memoria, entonces solo colocaríamos el nombre del
arreglo en la llamada a la función, y el prototipo y la
definición de la función deben tener un puntero que reciba
la dirección.
Ejemplo:
void lee_ahorro(float *);
void main()
{
float mes[31];
lee_ahorro(mes);
}
void lee_ahorro(float *m)
{
}
26
M. C. Bertha López Azamar
En C existe una relación tal entre punteros y arreglos,
que cualquier operación que se pueda realizar mediante
la indexación de un array, se puede realizar también con
punteros.
La diferencia radica en la expresión para acceder a
los elementos del arreglo:
lista[ind]
ó
*(lista + ind)
lo que es:
arreglo[indice]
ó
*(arreglo + indice)
En el arreglo del ejemplo sería:
mes[i]
ó
*(mes + i)
27
M. C. Bertha López Azamar
Dentro de la función el código que haga referencia al arreglo se
maneja igual, mediante un índice se referencia la posición. (en
el scanf se coloca el ampersan como siempre, ya que debe
localizar la dirección de la referencia)
Ejemplo:
void lee_ahorro(float *);
void main()
{
float mes[31];
lee_ahorro(mes);
}
void lee_ahorro(float *m)
{
m[2] = 10;
scanf("%d", &m[2]);
}
28
M. C. Bertha López Azamar
En el caso de las cadenas de caracteres, se manejan
indiferentemente como arrays, y se puede aplicar lo
expuesto para los arrays. Particularmente, puede
mencionarse que para hacer referencia a una cadena
de caracteres, podemos utilizar un puntero o un array:
/*utilizando un puntero*/
char *nombre = “Benito Juárez”;
/*utilizando un arreglo*/
char nombre[ ] = “Benito Juárez”;
29
M. C. Bertha López Azamar
ARRAYS DINÁMICOS
Utilizando la función malloc() y otras que se presentan para la
asignación de memoria dinámica, es posible definir arrays en
tiempo de ejecución, denominados también arrays dinámicos.
Ejemplo:
int i, n_elementos, *a;
scanf(“%d “, &n_elementos);
a=(int *)malloc(n_elementos * sizeof(int));
for(i=0; i < n_elementos; i++)
scanf(“%d”, &a[i]);
30
El programa completo es el siguiente:
M. C. Bertha López Azamar
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
void main()
{
int i, n_elementos, *a;
printf("Introduce el número de elementos para crear el arreglo : ");
scanf("%d", &n_elementos);
/*Asignacion del espacio de memoria para el puntero */
a=(int *)malloc(n_elementos * sizeof(int));
}
for(i=0; i < n_elementos; i++)
{
printf("%d.- ",i+1);
scanf("%d", (a+i));
/*en el scanf se coloca (a+i) pero pudo
emplearse &a[i] también */
}
system("cls");
for(i=0; i < n_elementos; i++)
{
printf("%d.- %d ",i+1, *(a+i)); /*aqui debe colocarse * antes */
}
getch();
31
Paso de parametros puntero
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
void llena(int *, int);
void imprime(int *, int);
void main()
{
int i, n_elementos, *a;
printf("Introduce el número de “);
printf(“elementos para crear el arreglo : ");
scanf("%d", &n_elementos);
/*Asignacion del espacio de
memoria para el puntero */
a=(int *)malloc(n_elementos * sizeof(int));
llena(a, n_elementos);
system("cls");
imprime(a, n_elementos);
}
getch();
M. C. Bertha López Azamar
void llena(int *arr, int num)
{
int i;
for(i=0; i < num; i++)
{
printf("%d.- ",i+1);
scanf("%d", (arr+i));
/*en el scanf se coloca (a+i) pero pudo
emplearse &a[i] también */
}
}
void imprime(int *arr, int num)
{
int i;
for(i=0; i < num; i++)
{
printf("%d.- %d ",i+1, *(arr+i));
/*aqui debe colocarse * antes */
}
}
32
Descargar