Está en la página 1de 8

Los punteros en C

1 Introduccin

Cmo se organiza la memoria asociada a un programa?
Como una coleccin de posiciones de memoria consecutivas. En ellas se almacenan los distintos tipos
de datos, que ocupan, por ejemplo:
1 char =1 byte
1 int =2 bytes
1 float =4 bytes

Un puntero no es ms que una variable esttica cuyo contenido es una direccin de memoria.

Los punteros, por lo tanto, guardan en dos o cuatro posiciones de memoria, la direccin de un conjunto
de celdas.



p C

donde:
pc es un puntero a carcter char *pc;
c es una variable de tipo carcter char c;

Inicialmente un puntero no apunta a ningn sitio. En C el valor NULL se reserva para indicar que el
puntero est vaco (equivalente al nil de la teora).

Operadores asociados a punteros
&: me da la direccin de un objeto en la memoria.
Slo sirve para posiciones de memoria (puede apuntar a variables o a vectores, pero no a constantes o
expresiones). Ejemplo:

/* prog1.c*/
pc = &c;
pr i nt f ( \ nNo t i ene l o mi smo %c que %d, c, pc) ; / * Oj o, %d par a pc*/

*: me da el contenido de una posicin de memoria (generalmente almacenada en un puntero). Se le
llama tambin operador indireccin. Por lo tanto es equivalente:

pr i nt f ( \ nTi ene l o mi smo %d que %d, &c, pc) ; / * Di r ecci ones */
pr i nt f ( \ nTi ene l o mi smo %c que %c, c, *pc) ; / * Car act er es */

Un puntero siempre est asociado a objetos de un tipo slo puede apuntar a objetos (variables o
vectores) de ese tipo.

i nt *i p; / * Sl o puede apunt ar a var i abl es ent er as */
char *c; / * Sl o puede apunt ar a var i abl es car ct er */
doubl e *dp, / * dp sl o puede apunt ar a var i abl es r eal es */
at of ( char *) ; / * at of es una f unci n que devuel ve un r eal
dada una cadena que se l e pasa como
punt er o a car ct er */

Los operadores * y & son unarios y tienen ms prioridad a la hora de evaluarlos que los operadores
binarios.
/* prog2.c */

i nt y, *i p;
y = 12;
pr i nt f ( \ nVal or de y %d, de i p %d, y, i p) ; / *si n asi gnar i p */
i p = &y;
*i p = *i p + 10;
pr i nt f ( \ nVal or de y %d, de *i p %d y de i p %d, y, *i p, i p) ;
y = *i p + 10;
pr i nt f ( \ nVal or de y %d, de *i p %d, y, *i p) ;
*i p += 1;
pr i nt f ( \ nVal or de y %d, de *i p %d, y, *i p) ;

Es necesario utilizar parntesis cuando aparecen en la misma expresin que otros operadores unarios
como ++o --, ya que en ese caso se evaluaran de izquierda a derecha.

++*i p;
( *i p) ++;

Dado que los punteros son variables, tambin pueden usarse como asignaciones entre direcciones. As:

int *ip, *iq;
iq =ip; /* Indica que iq apunta a donde apunte el puntero ip. */

2 Los punteros y los argumentos a funciones

Recordemos la equivalencia:

Cdigo Algortmico Equivalente C Ejemplo en C
parmetro dato paso por valor int scanf (%d, &entero)
parmetro resultado paso por referencia/ valor
devuelto por una funcin
int scanf (%d, &entero)
parmetro dato-resultado paso por referencia int scanf (%d, &entero)

En C, por defecto, todos los parmetros a las funciones se pasan por valor (la funcin recibe una copia
del parmetro, que no se ve modificado por los cambios que la copia sufra dentro de la funcin).
Ejemplo: Intercambio de dos valores

{VERSION ERRONEA}
i nt er cambi a ( i nt a, i nt b) {
i nt t mp;

t mp = a;
a = b;
b = t mp
}
{VERSION CORRECTA}
i nt er cambi a ( i nt *a, i nt *b) {
i nt t mp;

t mp = *a;
*a = *b;
*b = t mp
}

Para que un parmetro de una funcin pueda ser modificado, ha de pasarse por referencia, y en
C eso slo es posible pasando la direccin de la variable en lugar de la propia variable.
Si se pasa la direccin de una variable, la funcin puede modificar el contenido de esa posicin (no as
la propia direccin, que es una copia).
3 Punteros y vectores

En C los punteros y los vectores estn fuertemente relacionados, hasta el punto de que el nombre de un
vector es en s mismo un puntero a la primera (0-sima) posicin del vector. Todas las operaciones que
utilizan vectores e ndices pueden realizarse mediante punteros.

int v[10];

1 3 5 7 9 11 13 15 17 19
v[0] v[1] v[2] v[3] v[4] v[5] v[6] v[7] v[8] v[9]

v: designa 10 posiciones consecutivas de memoria donde se pueden almacenar enteros.

int *ip;

Designa un puntero a entero, por lo tanto puede acceder a una posicin de memoria. Sin embargo,
como se ha dicho antes v tambin puede considerarse como un puntero a entero, de tal forma que las
siguientes expresiones son equivalentes:

ip =&v[0] ip =v
x =*ip; x =v[0];
*(v +1) v[1]
v +I &v[i]

4 Aritmtica de punteros

El compilador C es capaz de adivinar cul es el tamao de una variable de tipo puntero y realiza los
incrementos/decrementos de los punteros de la forma adecuada. As:

int v[10], *p;
p =v;

/* p Apunta a la posicin inicial del vector*/
/* p +0 Apunta a la posicin inicial del vector*/
/* p +1 Apunta a la segunda posicin del vector*/
/* p +i Apunta a la posicin i+1 del vector*/

p =&v[9]; /* p apunta ahora a la ltima posicin (dcima) del vector */

/* p - 1 Apunta a la novena posicin del vector*/
/* p - i Se refiere a la posicin 9 - i en v*/

Ejemplo de recorrido de un vector utilizando ndices y punteros.
/* prog3.c*/
main( ) {
i nt v[ 10] ;
i nt i , *p;

f or ( i =0; i < 10; i ++) v[ i ] = i ;

f or ( i =0; i < 10; i ++) pr i nt f ( " \ n%d" , v[ i ] ) ;

p = v;
f or ( i =0; i < 10; i ++) pr i nt f ( " \ n%d" , *p++) ;
/ * Tr as cada p++ el punt er o seal a a l a si gui ent e posi ci n en v */
}
C realiza las mismas operaciones con independencia del tipo de la variable.
De esta forma, si tenemos dos variables que son punteros:

int *ip, vi[10]; /* Necesita dos posiciones para representar un entero */
char *cp, vc[10]; /* Necesita una posicin para representar un carcter */

ip =&vi[0];
cp =&vc[0];

Las expresiones:

ip +1, ip++ Apunta al siguiente elemento (dos posiciones): vi[1]
cp +1, cp++ Apunta al siguiente elemento (una posicin): vc[1]

Diferencia entre puntero y nombre de vector: Mientras que un puntero es una variable y puede
intervenir en asignaciones o incrementos, el nombre del vector no es una variable, por lo tanto,
no pueden realizarse operaciones del tipo:

int v[10], *a;
v =a;
v++;

Dado que el nombre del vector es un puntero, puede utilizarse para pasar un vector como parmetro de
una funcin (lo que permite modificar el contenido de esas posiciones de memoria adyacentes, o lo
que es lo mismo, los elementos del vector).

char palabra[10];

scanf (%s, palabra); /* Modifica las posiciones de memoria */
sort (palabra, 0, strlen(palabra));

/* sort va a modificar el contenido del vector, por lo tanto se pasar la direccin del primer elemento =
palabra =&palabra[0].
strlen es una funcin de la librera estndar, cuyo macro es: (int strlen (char *))
La funcin nos devuelve la longitud de un vector de caracteres */

Recordatorio: Los vectores de caracteres terminan en el carcter \0.

Ejemplo 1: Realizar una funcin que sea equivalente a strlen. i nt l ongi t ud ( char *)

char cadena[20];

gets(cadena);
printf (\nLa longitud de la cadena %s es %d, cadena, longitud(cadena));

Ejemplo 2: Realizar una funcin equivalente que permita realizar a :=b, cuando ambas son cadenas.
copi a ( char *, char *)

char cadena[20], *copiada;

printf (\nDame la primera cadena);
gets (cadena);
copia (cadena, copiada);
printf (\nLa segunda cadena es %s, copiada);
Solucin a 1):

main( ) {
char cadena[ 20] ;
i nt l ongi t ud( ) ;

pr i nt f ( " \ nDame una cadena ( maxi mo 20) : " ) ;
get s( cadena) ;
pr i nt f ( " \ nLa cadena %s mi de %d " , cadena, l ongi t ud( cadena) ) ;
}

i nt longitud( char *s) {
i nt l ;

l = 0;
whi l e ( *s++ ! = ' \ 0' ) l ++;

r et ur n l ;
}

Solucin a 2):

main( ) {
char uno[ 20] , dos[ 20] ;

pr i nt f ( " \ nDame una cadena: " ) ;
get s( uno) ;

copi a( uno, dos) ;

pr i nt f ( " \ nLa copi a de %s\ n es %s\ n" , uno, dos) ;
}

copia ( char *s, char *p)
{
whi l e ( *s) *p++ = *s++;
*p = ' \ 0' ;
}

5 Asignacin dinmica de memoria

Hasta el momento slo se ha visto cmo el lenguaje C define y utiliza los punteros para
acceder a las posiciones de memoria asignadas a un programa. Sin embargo, no se ha tratado
cmo conseguir nuevas posiciones de memoria (atenindose al lenguaje de la parte
algortmica: cmo funciona el Mdulo de Gestin de la Asignacin Dinmica de C).

En la <stdlib.h>estn definidas las siguientes funciones:

voi d *cal l oc( si ze_t nobj , si ze_t si ze)
calloc obtiene (reserva) espacio en memoria para alojar un vector (una coleccin) de nobj
objetos, cada uno de ellos de tamao size. Si no hay memoria disponible se devuelve NULL.
El espacio reservado se inicializa a bytes de ceros.
Obsrvese que calloc devuelve un (void *) y que para asignar la memoria que devuelve a un
tipo Tipo_t hay que utilizar un operador de ahormado: (Tipo_T *)

Ejemplo:
char * c;
c = ( char *) cal l oc ( 40, si zeof ( char ) ) ;

voi d *mal l oc( si ze_t si ze)
mal l oc f unci ona de f or ma si mi l ar a cal l oc sal vo que: a) no i ni ci al i za
el espaci o y b) es necesar i o saber el t amao exact o de l as posi ci ones
de memor i a sol i ci t adas.

El ejemplo anterior se puede reescribir:
char * c;
c = ( char *) mal l oc ( 40*si zeof ( char ) ) ;

voi d *r eal l oc( voi d *p, si ze_t si ze)
realloc cambia el tamao del objeto al que apunta p y lo hace de tamao size. El contenido de
la memoria no cambiar en las posiciones ya ocupadas. Si el nuevo tamao es mayor que el
antiguo, no se inicializan a ningn valor las nuevas posiciones. En el caso en que no hubiese
suficiente memoria para realojar al nuevo puntero, se devuelve NULL y p no vara.
El puntero que se pasa como argumento ha de ser NULL o bien un puntero devuelto por
malloc(), calloc() o realloc().

#def i ne N 10
#i ncl ude <st di o. h>

mai n( ) {
char c, *cambi ant e;
i nt i ;

i =0;
cambi ant e = NULL;

pr i nt f ( " \ nI nt r oduce una f r ase. Ter mi nada en [ ENTER] \ n" ) ;
whi l e ( ( c=get char ( ) ) ! = ' \ n' ) {
i f ( i %N == 0) {
pr i nt f ( " \ nLl ego a %d posi ci ones y pi do hast a %d" , i , i +N) ;
cambi ant e=( char *) r eal l oc( ( char *) cambi ant e, ( i +N) *si zeof ( char ) ) ;
i f ( cambi ant e == NULL) exi t ( - 1) ;
}
/ * Ya exi st e suf i ci ent e memor i a par a el si gui ent e car ct er */
cambi ant e[ i ++] = c;
}


/ * Ant es de poner el t er mi nador nul o hay que asegur ar se de que haya
suf i ci ent e memor i a */
i f ( ( i %N == 0) && ( i ! = 0) ) {
pr i nt f ( " \ nLl ego a %d posi ci ones y pi do hast a %d" , i , i +N) ;
cambi ant e=r eal l oc( ( char *) cambi ant e, ( i +N) *si zeof ( char ) ) ;
i f ( cambi ant e == NULL) exi t ( - 1) ;
}
cambi ant e[ i ] =0;

pr i nt f ( " \ nHe l ei do %s" , cambi ant e) ;
}

voi d f r ee( voi d *p)
free() libera el espacio de memoria al que apunta p. Si p es NULL no hace nada. Adems p
tiene que haber sido alojado previamente mediante malloc(), calloc() o realloc().


NO MIRAR ESTA PARTE HASTA ENTENDER PERFECTAMENTE LO ANTERIOR

El siguiente programa tambin soluciona el anterior problema 2. La diferencia entre ambas
soluciones est en que el nuevo vector no tiene un tamao fijo, sino que se le asigna en
funcin del tamao del vector original.

main( ) {
char uno[ 20] , *dos;

pr i nt f ( " \ nDame una cadena: " ) ;
get s( uno) ;

copi a2( uno, &dos) ; / *I ni ci al ment e dos no apunt a a ni ngn si t i o*/
/ *En copi a2 se modi f i car el val or de dos */
pr i nt f ( " \ nLa copi a de %s\ n es %s\ n" , uno, dos) ;
}

/ * s es un punt er o a char . Su val or ( l a di r ecci n) no es modi f i cada)
p es un punt er o a un punt er o a char . Su val or ( un punt er o a char ,
i ni ci al ment e vac o) SI que se va a modi f i car dent r o */

copia2 ( char *s, char **p)
{
i nt i ;
char *r ;

/ * *p es el punt er o al que se l e asi gnan nuevas posi ci ones de memor i a*/
*p = ( char *) cal l oc ( st r l en( s) +1, si zeof ( char ) ) ;

/ * Ut i l i zo r par a cambi ar el cont eni do de *p */
r = *p;
whi l e ( *s) *r ++ = *s++;
*r = 0;
}

Si no hubiese utilizado el puntero r , y hubiese hecho avanzar el puntero *p igual que se hizo
en el ejemplo de l ongi t ud( ) , habra perdido las posiciones anteriores a *p. Otras dos formas
de realizar el mismo ejercicio seran:
main( ) {
char uno[ 20] , *dos;
char t r es[ 20] , *cuat r o;

pr i nt f ( " \ nDame una cadena: " ) ;
get s( u
copia3( uno, &dos) ;
no) ;
pr i nt f ( " \ nLa copi a de %s\ n es %s\ n" , uno, dos) ;

pr i nt f ( " \ nDame ot r a cadena: " ) ;
get s( t
copia4( t r es, &cuat r o) ;
r es) ;
pr i nt f ( " \ nLa copi a de %s\ n es %s\ n" , t r es, cuat r o) ;
}

/ *Ut i l i zo un ndi ce par a r ecor r er *p y cambi ar **p si n modi f i car *p */
copia3 ( char *s, char **p)
{
i nt i ;

*p = ( char *) cal l oc ( st r l en( s) +1, si zeof ( char ) ) ;

i =0;
whi l e ( *s) ( *p) [ i ++] = *s++;
( *p) [ i ] = 0;
}

/ *Ut i l i zo el ndi ce, per o accedo medi ant e *( *p + i ) en vez de ( *p) [ i ] */
copia4 ( char *s, char **p)
{
i nt i ;

*p = ( char *) cal l oc ( st r l en( s) +1, si zeof ( char ) ) ;
i =0;
whi l e( *s) {
*( *p + i ) = *s++;
i ++;
}
*( *p + i ) = 0;
}

También podría gustarte