Punteros en C - Instituto de Estadística – Pontificia Universidad

Anuncio
Punteros en C
Felipe Osorio
http://www.ies.ucv.cl/fosorio
Instituto de Estadı́stica
Pontificia Universidad Católica de Valparaı́so
Abril 6, 2015
1 / 16
Punteros en C
I Su uso produce código compacto y eficiente.
I Requerido para el paso de parámetros por referencia a funciones.
I Permiten la asignación de dinámica de memoria.
I Fundamentales para manipular (eficientemente) ciertas estructuras de datos.
I Estrecha relación entre punteros y arreglos.
I Se pueden “apuntar” a cualquier tipo de variable.
2 / 16
Dirección de memoria
Cuando una variable es declarada se asocian tres caracterı́sticas fundamentales:
I Nombre.
I Tipo.
I Dirección de memoria.
Considere el siguiente ejemplo:
int n = 10;
/* imprime valor de ’n ’ */
printf ( " % i " , n );
/* imprime la dirección de ’n ’ */
printf ( " % p " , & n );
Salida:
10
0 x7fffaac38e0c
3 / 16
Dirección de memoria
Cuando una variable es declarada se asocian tres caracterı́sticas fundamentales:
I Nombre.
I Tipo.
I Dirección de memoria.
Considere el siguiente ejemplo:
int n = 10;
/* imprime valor de ’n ’ */
printf ( " % i " , n );
/* imprime la dirección de ’n ’ */
printf ( " % p " , & n );
Salida:
10
0 x7fffaac38e0c
3 / 16
Dirección de memoria
Cuando una variable es declarada se asocian tres caracterı́sticas fundamentales:
I Nombre.
I Tipo.
I Dirección de memoria.
Considere el siguiente ejemplo:
int n = 10;
/* imprime valor de ’n ’ */
printf ( " % i " , n );
/* imprime la dirección de ’n ’ */
printf ( " % p " , & n );
Salida:
10
0 x7fffaac38e0c
3 / 16
Operación básica y declaración
I Operadores unarios:
& obtiene la dirección de memoria.
* obtiene el contenido del objeto apuntado por el puntero
(indirección/dereferencia).
I Declaración:
tipo * no mbre_pu ntero [= expresión ];
El tipo de datos en la declaración de un puntero debe concordar con la variable
a la que se desea apuntar.
4 / 16
¿Qué hacen los punteros? Diagrama de memoria
Código fuente
int n = 10;
int *ptr = &n;
Contenido de memoria
Dirección
10
0x7fffccf6e534
0x7fffccf6e534
0x7fffccf6e538
5 / 16
¿Qué hacen los punteros? Puntero a entero
# include < stdio .h >
main ()
{
int n = 10;
int * ptr = & n ;
printf ( " % i \ n " , n );
/* imprime el valor de ’n ’ */
printf ( " % p \ n " , & n );
printf ( " % p \ n " , ptr );
/* obtiene dirección de ’n ’ */
/* imprime contenido de ’ ptr ’ */
printf ( " % p \ n " , & ptr ); /* obtiene dirección de ’ ptr ’ */
return ;
}
Salida:
10
0 x7fffccf6e534
0 x7fffccf6e534
0 x7fffccf6e538
6 / 16
¿Qué hacen los punteros? Puntero a entero
# include < stdio .h >
main ()
{
int n = 10;
int * ptr = & n ;
printf ( " % i \ n " , n );
/* imprime el valor de ’n ’ */
printf ( " % p \ n " , & n );
printf ( " % p \ n " , ptr );
/* obtiene dirección de ’n ’ */
/* imprime contenido de ’ ptr ’ */
printf ( " % p \ n " , & ptr ); /* obtiene dirección de ’ ptr ’ */
return ;
}
Salida:
10
0 x7fffccf6e534
0 x7fffccf6e534
0 x7fffccf6e538
6 / 16
¿Qué hacen los punteros? Tenga presente que...
Para recalcar:
I Un puntero es una variable que ‘actua’ como una referencia a un objeto o
función.
I La mayorı́a de las veces el valor del puntero no es de interés en sı́ mismo.
I Lo verdaderamente importante es la dirección de memoria a la que apunta.
7 / 16
Inicialización de punteros
# include < stdio .h >
main ()
{
int n = 10;
int * ptr = NULL ;
printf ( " % i \ n " , n );
printf ( " % p \ n " , ptr );
ptr = & n ; /* asigna la dirección de ’n ’ a ’ ptr ’ */
* ptr = 50; /* asigna un valor a la dirección de memoria */
printf ( " % i \ n " , n );
printf ( " % p \ n " , ptr );
return ;
}
8 / 16
Inicialización de punteros
I Al declarar un puntero, (tal como cualquier otra variable) su valor no está
definido.
I No inicializar un puntero puede ser peligroso.
I Es recomendable inicializar sus punteros como NULL.
I Un error frecuente es no asignar una dirección válida a un puntero antes
de usarlo.
9 / 16
Operaciones con punteros
I Asignación: indica que ámbos punteros apuntan a la misma dirección de
memoria.
int x = 4 , * p1 , * p2 ;
p1 = & x ;
p2 = p1 ;
/* p2 apunta a la dirección de ’x ’ */
I Comparación: Podemos saber si un puntero apunta a la misma dirección de
memoria usando p2 == p1 (o si son distintas p2 != p1)
Por otro lado, p1 < p2 indica que p1 apunta a una dirección de memoria más
baja que p2 (no confundir con *p1 < *p2).
10 / 16
Aritmética de punteros
Sumar o restar un entero a un puntero tiene el efecto de incrementar (disminuir) la
dirección de memoria contenida por dicho puntero.
# include < stdio .h >
main ()
{
double x = 1.2;
double * ptr , * ptr_add , * ptr_sub ;
ptr = & x ;
ptr_add = ptr + 3;
ptr_sub = ptr_add - 3;
printf ( " x
:
printf ( " ptr
:
printf ( " ptr_add :
printf ( " ptr_sub :
printf ( " \ n " );
%g\n",
%p\n",
%p\n",
%p\n",
x );
ptr );
ptr_add );
ptr_sub );
if ( ptr == ptr_sub )
printf ( " ptr y ptr_sub son iguales \ n " );
return ;
}
11 / 16
Aritmética de punteros
Sumar o restar un entero a un puntero tiene el efecto de incrementar (disminuir) la
dirección de memoria contenida por dicho puntero.
# include < stdio .h >
main ()
{
double x = 1.2;
double * ptr , * ptr_add , * ptr_sub ;
ptr = & x ;
ptr_add = ptr + 3;
ptr_sub = ptr_add - 3;
printf ( " x
:
printf ( " ptr
:
printf ( " ptr_add :
printf ( " ptr_sub :
printf ( " \ n " );
%g\n",
%p\n",
%p\n",
%p\n",
x );
ptr );
ptr_add );
ptr_sub );
if ( ptr == ptr_sub )
printf ( " ptr y ptr_sub son iguales \ n " );
return ;
}
11 / 16
Intercambiar dos números: Programa INÚTIL1
# include < stdio .h >
/* declaracion de funciones */
void i n t e r c a m b i a r _ 2 _ n u m e r o s ( double , double );
main ()
{
/* función ’ principal ’ */
double a = 0. , b = 1.;
printf ( " a = %g , b = % g \ n " , a , b );
printf ( " \ n I nt er c am bi an d o ...\ n " );
i n t e r c a m b i a r _ 2 _ n u m e r o s (a , b );
printf ( " a = %g , b = % g \ n " , a , b );
return ;
}
void i n t e r c a m b i a r _ 2 _ n u m e r o s ( double a , double b )
{
double x ;
/* a <-> b */
x = a;
a = b;
b = x;
return ;
}
1 Esto se denomina: llamada-por-valor
12 / 16
Intercambiar dos números: Usando punteros2
# include < stdio .h >
/* declaracion de funciones */
void i n t e r c a m b i a r _ 2 _ n u m e r o s ( double * , double *);
main ()
{
/* función ’ principal ’ */
double a = 0. , b = 1.;
printf ( " a = %g , b = % g \ n " , a , b );
printf ( " \ n I nt er c am bi an d o ...\ n " );
i n t e r c a m b i a r _ 2 _ n u m e r o s (& a , & b );
printf ( " a = %g , b = % g \ n " , a , b );
return ;
}
void i n t e r c a m b i a r _ 2 _ n u m e r o s ( double *a , double * b )
{
double x ;
/* a <-> b */
x = *a;
*a = *b;
*b = x;
return ;
}
2 Esto se denomina: llamada-por-referencia
13 / 16
Punteros y arreglos
I Los arreglos y punteros estan fuertemente relacionados.
I El nombre de un arreglo es un ı́ndice (o un puntero) a la dirección de inicio del
arreglo.
I En esencia el nombre de un arreglo es un puntero a un arreglo.
I En la práctica se pueden entender arreglos como si fueran punteros y punteros
como si fueran arreglos.
14 / 16
Funciones axpy y print mat
void axpy ( double *y , double a , double *x , int n )
{
/* y <- a * x + y */
int i ;
if ( n <= 0) return ;
if ( a == 0.0) return ;
for ( i = 0; i < n ; i ++)
* y ++ += a * * x ++;
}
void print_mat ( char * msg , double *x , int ldx , int nrow , int ncol )
{
/* print matrix and message */
int i , j ;
printf ( " % s \ n " , msg );
for ( i = 0; i < nrow ; i ++) {
for ( j = 0; j < ncol ; j ++) {
printf ( " %6.4 g " , x [ i + j * ldx ]);
}
printf ( " \ n " );
}
printf ( " \ n " );
}
15 / 16
Funciones axpy y print mat
void axpy ( double *y , double a , double *x , int n )
{
/* y <- a * x + y */
int i ;
if ( n <= 0) return ;
if ( a == 0.0) return ;
for ( i = 0; i < n ; i ++)
* y ++ += a * * x ++;
}
void print_mat ( char * msg , double *x , int ldx , int nrow , int ncol )
{
/* print matrix and message */
int i , j ;
printf ( " % s \ n " , msg );
for ( i = 0; i < nrow ; i ++) {
for ( j = 0; j < ncol ; j ++) {
printf ( " %6.4 g " , x [ i + j * ldx ]);
}
printf ( " \ n " );
}
printf ( " \ n " );
}
15 / 16
Que son invocadas desde el programa principal
# include < stdio .h >
/* declaración de funciones */
void axpy ( double * , double , double * , int );
void print_mat ( char * , double * , int , int , int );
main ()
{
int i , n = 10;
double x [10] = {0.0} , y [15] = {0.0} , a = -1.0;
/* in icializ ación */
x [0] = 0.0; x [1] = 1.0;
for ( i = 2; i < n ; i ++) {
x [ i ] = x [i -2] + x [i -1];
y [ i ] = ( double ) i ;
}
/* llamada a ’ print_mat ’ */
print_mat ( " x : " , x , 1 , 1 , 10);
print_mat ( " y : " , y , 1 , 1 , 10);
/* llamada a ’ axpy ’ */
axpy (y , a , x , n );
/* llamada a ’ print_mat ’ */
print_mat ( " x : " , x , 1 , 1 , 10);
print_mat ( " y : " , y , 1 , 1 , 10);
return ;
}
16 / 16
Descargar