You are on page 1of 7

Punteros o Apuntadores

Un puntero en C es una variable cuyo contenido es una direccin de memoria. Normalmente esta
direccin es una posicin de otra variable en la memoria. Los punteros son muy usados en C por las
siguientes razones:

Accesar y manipular un string.


Gran velocidad de ejecucin usando punteros, al trabajar con grandes bloques de datos.
El uso de punteros puede dar como resultado cdigos ms compactos y eficientes.
Indirectamente retorna ms de un valor desde una funcin.

C usa apuntadores explcitamente con:


Arreglos,
Estructuras y
Funciones

Variables tipo puntero


Un apuntador es una variable que contiene la direccin en memoria de otra variable. Se pueden tener
apuntadores a cualquier tipo de variable.
Si una variable va ha contener un puntero entonces se debe declarar como tal. La declaracin
consiste en un tipo base, el carcter asterisco (*) y el nombre de la variable ejemplo:
sintaxis: tipo * identificador;
char *text;
int num, *conta, destino;
El asterisco antes de conta le dice al compilador que sta variable es un puntero y como es declarado
de tipo int el compilador sabr que la variable contendr la direccin de una variable entera.
Operadores & y *

&:- Es un operador unario (ampersan) (requiere de un solo operando) ste operador devuelve la
direccin de memoria de su operando.
*:- Es un operador de indireccin o dereferencia, devuelve el valor de la variable situada en la
direccin que contiene, ejemplos:
num = 5
conta = #
destino = *conta

/*asigna la direccin de num */


/*asigna el valor de num a destino */

Adems devuelve el contenido de un objeto apuntado por un apuntador''.

Asignacin de Punteros
Despus de declarar un puntero, pero antes de asignarle un valor, el puntero contiene un valor
desconocido. Si se intenta utilizar el puntero antes de darle valor, se producir un fallo en el programa.
Un puntero que no apunte a ninguna posicin vlida de la memoria ha de tener asignado el valor nulo, que
es un cero. Se usa el valor nulo porque C++ garantiza que no existe ningn objeto en la direccin nula. As,
cualquier puntero que sea nulo indica que no apunta a nada y no debe ser usado.
Una forma de dar a un puntero el valor nulo es asignndole el valor cero. Por ejemplo, lo siguiente inicializa
el puntero p a nulo:
char *p = 0;

Adems, muchos de los archivos de cabecera de C++, como <cstdio>, definen NULL como el valor de
puntero nulo. Por eso tambin es posible ver la inicializacin de un puntero a nulo usando la constante
NULL (aunque realmente con esto le estamos asignando un 0). char *p = NULL;
NOTA: Un apuntador a cualquier tipo de variables es una direccin en memoria -- la cual es una direccin
entera, pero un apuntador NO es un entero.
La razn por la cual se asocia un apuntador a un tipo de dato, es por que se debe conocer en cuantos bytes
esta guardado el dato. De tal forma, que cuando se incrementa un apuntador, se incrementa el apuntador
por un ``bloque'' de memoria, en donde el bloque esta en funcin del tamao del dato.
Por lo tanto para un apuntador a un char, se agrega un byte a la direccin y para un apuntador a entero
largoo a flotante se agregan 4 bytes. De esta forma si a un apuntador a flotante se le suman 2, el apuntador
entonces se mueve dos posiciones float que equivalen a 8 bytes.

Utilizacin de punteros para comunicacin entre funciones (Parmetros )


Cuando se pasa un puntero hacia una funcin se pasa una copia de su valor, pero el valor que se
pasa en este caso es una direccin. En general se pueden enviar a las funciones 2 tipos de informacin
acerca de una variable los tipos de envos son:

Llamada por valor.


Llamada por referencia.

Llamada por valor


Se utiliza si la funcin necesita el valor para hacer algn clculo o alguna accin ejemplo:
funcin (valor); /* llamada a la funcin */
funcin (tipo variable)
/* definicin */
Ejemplo:
void inter1(int a,int b)
{
int temp;
temp = a;
a = b;
b = temp;
}

// paso por valor

Llamada por referencia


Se utiliza si la funcin necesita alterar las variables del programa de llamada ejemplo:
funcin (&variable,&...); /* llamada a la funcin */
funcin (int *num1, int *num2) /* definicin */
Ejemplo:
void inter2(int *a,int *b) // paso por referencia1
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
Llamada por referencia para C++
Se utiliza si la funcin necesita alterar las variables del programa de llamada ejemplo:

funcin (variable1,...); /* llamada a la funcin */


funcin (int &num1, int &num2) /* definicin */
Ejemplo:
void inter3(int &a, int &b)
{
int temp;
temp = a;
a = b;
b = temp;
}

// uso de las direcciones

Punteros y Arreglos
En C existe una estrecha relacin entre punteros y arreglos suficientemente estrechas para que se
los trate simultneamente. Cualquier operacin que se pueda realizar mediante la indexacin de un arreglo
se puede realizar en base a punteros. En efecto el nombre del arreglo representa la direccin del elemento 0
del arreglo. La versin con punteros ser ms eficiente y por consiguiente ms rpida.
Lenguaje C permite dos mtodos de acceso a los elementos de un arreglo. Este hecho es importante
por que la aritmtica de punteros es ms rpida que los ndices de arreglos. Como la velocidad es un factor
importante en la programacin el uso de punteros para acceder a los elementos de un arreglo es comn en
C.
Indexando un puntero
En C se puede indexar un puntero como si fuera un arreglo. Esto indica nuevamente la estrecha
relacin entre punteros y arreglos, ejemplo:
int num[20], *pa;
pa=num;*/

/* si tenemos esto vemos que num[i]==*(pa+i) es idntico a


pa=num;

Al aplicar el operador & a las dos partes de esta equivalencia &num[i]==num+i, se deduce que
&num[i] es lo mismo que num+i donde num+i es la direccin del i-simo elemento de num.
Por
otra
parte si pa es un apuntador en las expresiones puede presentarse con un sub ndice pa[i]==*(pa+i). Entonces
tenemos que en cualquier expresin en la que se tenga un arreglo y un sub ndice se puede escribir como un
puntero y un desplazamiento y viceversa.
Sin embargo existe una diferencia entre el nombre de un arreglo y un puntero.
Un apuntador es una variable por lo que operaciones como:
pa=num;
pa++;
/* son operaciones correctas. Pero el nombre de un arreglo es una direccin o puntero fijo de
ah que expresiones como las siguientes son totalmente ilegales */
++num;
num+=2;
En esencia, el nombre de un arreglo es un puntero al arreglo. Considerar lo siguiente:
int a[10], x;
int *ap;
ap = &a[0];
x = *ap;

/* ap apunta a la direccin de a[0] */


/* A x se le asigna el contenido de ap (a[0] en este caso) */

*(ap + 1) = 100; /* Se asigna al segundo elemento de 'a' el valor 100 usando ap*/

Como se puede observar en el ejemplo la sentencia a[t] es idntica a ap+t. Se debe tener cuidado ya que C no
hace una revisin de los lmites del arreglo, por lo que se puede ir fcilmente ms all del arreglo en memoria
y sobrescribir otras cosas.
C sin embargo es mucho ms sutil en su relacin entre arreglos y apuntadores. Por ejemplo se puede teclear
solamente:
ap = a; en vez de ap = &a[0]; y tambin *(a + i) en vez de a[i], esto es, &a[i] es equivalente con a+i.
Y como se ve en el ejemplo, el direccionamiento de apuntadores se puede expresar como:
a[i] que es equivalente a *(ap + i)
Sin embargo los apuntadores y los arreglos son diferentes:

Un apuntador es una variable. Se puede hacer ap = a y ap++.


Un arreglo NO ES una variable. Hacer a = ap y a++ ES ILEGAL.
Este parte es muy importante, asegrese haberla entendido.
Con lo comentado se puede entender como los arreglos son pasados a las funciones. Cuando un arreglo es
pasado a una funcin lo que en realidad se le esta pasando es la localidad de su elemento inicial en
memoria.
Por lo tanto:
strlen(s) es equivalente a strlen(&s[0])
Esta es la razn por la cual se declara la funcin como:
int strlen(char s[]); y una declaracin equivalente es int strlen(char *s);
ya que char s[] es igual que char *s.
La funcin strlen() es una funcin de la biblioteca estndar que regresa la longitud de una cadena. Se
muestra enseguida la versin de esta funcin que podra escribirse:
int strlen(char *s) {
char *p = s;
while ( *p != '\0' )
return p - s;
}

p++;

Se muestra enseguida una funcin para copiar una cadena en otra. Al igual que en el ejercicio anterior existe
en la biblioteca estndar una funcin que hace lo mismo.
void strcpy(char *s, char *t) {

while ( (*s++ = *t++) != '\0' ); }

Arreglos Como Argumentos De Funciones Y Punteros Como Argumentos


Dado que el nombre de un arreglo es en realidad una referencia a la direccin de comienzo de un
arreglo, el paso de un arreglo como argumento de una funcin es siempre un paso por referencia. Por lo
tanto los cambios que se hagan dentro de la funcin afectarn el arreglo original.
Como el compilador no tiene que reservar espacio de memoria para almacenar una copia del arreglo
no es necesario especificar el tamao del arreglo.

int tamanio(char *s1)


{
int x=0;
while(*s1){ s1++;
return x;
}

x++;

int strlen(char *pa)


{ int cont=0;
while(*pa++)
cont++;
return(cont);
}

Arreglos multidimensionales y apuntadores


Un arreglo multidimensional puede ser visto en varias formas en C, por ejemplo:
Un arreglo de dos dimensiones es un arreglo de una dimensin, donde cada uno de los elementos es en s
mismo un arreglo.
Por lo tanto, la notacin
rengln por rengln.

a[n][m]

nos indica que los elementos del arreglo estn guardados

Cuando se pasa una arreglo bidimensional a una funcin se debe especificar el nmero de columnas -- el
nmero de renglones es irrelevante.
La razn de lo anterior, es nuevamente los apuntadores. C requiere conocer cuantas son las columnas para
que pueda brincar de rengln en rengln en la memoria.
Considerando que una funcin deba recibir int a[5][35], se puede declarar el argumento de la funcin como:
f( int a[][35] ) { ..... }

o an

f( int (*a)[35] ) { ..... }

En el ltimo ejemplo se requieren los parnteis (*a) ya que [ ] tiene una precedencia ms alta que *.
Por lo tanto:
int (*a)[35]; declara un apuntador a un arreglo de 35 enteros, y por ejemplo si hacemos la siguiente
referencia a+2, nos estaremos refiriendo a la direccin del primer elemento que se encuentran en el tercer
rengln de la matriz supuesta, mientras que int *a[35]; declara un arreglo de 35 apuntadores a enteros.
Ahora veamos la diferencia (sutil) entre apuntadores y arreglos. El manejo de cadenas es una aplicacin
comn de esto.
Considera:

char *nomb[10];

char anomb[10][20];

En donde es vlido hacer nomb[3][4] y anomb[3][4] en C.


Sin embargo:
- anomb es un arreglo verdadero de 200 elementos de dos dimensiones tipo char.
- El acceso de los elementos anomb en memoria se hace bajo la siguiente frmula 20*renglon +
columna + direccin_base
- En cambio nomb tiene 10 apuntadores a elementos.
NOTA: si cada apuntador en nomb indica un arreglo de 20 elementos entonces y solamente entonces 200
chars estarn disponibles (10 elementos).
Con el primer tipo de declaracin se tiene la ventaja de que cada apuntador puede apuntar a arreglos de
diferente longitud.
Considerar:

char *nomb[] = { "No mes", "Ene", "Feb", "Mar", .... };

char anomb[][15] = { "No mes", "Ene", "Feb", "Mar", ... };


Lo cual grficamente se muestra en la figura 8.2. Se puede indicar que se hace un manejo ms eficiente del
espacio haciendo uso de un arreglo de apuntadores y usando un arreglo bidimensional.

Arreglo de 2 dimensiones VS. arreglo de apuntadores.

Punteros a Punteros
Como se ha visto un puntero es una variable que almacena direcciones y esta direccin es
usualmente la localizacin en memoria de otra variable. Sin embargo en C sta otra variable puede ser un
puntero.
Este no es el lmite de la relacin puntero si no que esto se puede extender de punteros a punteros,
etc. Pero esto nos llevara a un cdigo muy complicado de entender.

621

622

623

629
p

.. .

629

621

pp

num

puntero a puntero

int **pp, *p, num=3;


p=&num;
pp=&p;
ejemplo :
#define f 3
#define c 3

num imprime 3
*p imprime 3
**pp imprime 3 *pp

629

int i,j,mat1[f][c],**mat2;
..
// inicializar al arreglo mat con cero
for(i=0;i<f;i++)
for(j=0;j<c;j++)
mat1[i][j]=0;
// inicializar al arreglo de punteros mat2 con cero usando aritmtica de punteros.
for(i=0;i<f;i++)
for(j=0;j<c;j++)
*(*(mat2+i)+j)=0;
nota : ver memoria dinmica
Punteros de tipo void
Esta posibilidad ya est presente en el ANSI C pero conviene citarla aqu tambin. Un puntero a void es un
puntero que no conoce en el momento de su definicin a qu tipo de dato va a apuntar. Un buen ejemplo de
esto es el valor de retorno de la funcin malloc(). Esta funcin reserva memoria dinmicamente para
cualquier tipo de dato, incluso para aquellos tipos de datos que haya definido el usuario.

You might also like