Está en la página 1de 7

El paso de parmetros por referencia

Paso de parmetros por valor

El mecanismo que hemos estado utilizando hasta el momento para pasar parmetros del
programa principal a los procedimientos o funciones se denomina paso por valor. El
computador hace una copia del valor de los parmetros que el programa principal quiere
pasar al procedimiento o funcin, y el procedimiento o funcin trabaja con esta copia
del valor de los parmetros. Veamos un ejemplo.

#include <stdio.h>
#include <string.h>
typedef char Tpalabra [20];
typedef struct {
Tpalabra nombre;
int edad;
int nota;
} Talumno;
void escribe_datos (Talumno a)
{
printf ("Nombre: %s\n", a.nombre);
printf ("Edad: %d\n", a.edad);
printf ("Nota: %d\n", a.nota);
}
void main ()
{
Talumno alum;
strcpy (alum.nombre, "Juan");
alum.edad = 20;
alum.nota = 6;
escribe_datos (alum);
}

En este ejemplo se ha definido un procedimiento que escribe en pantalla los datos de


una estructura (nombre, edad y nota de un alumno). El programa principal declara una
variable de tipo Talumno, se asigna un valor a cada uno de sus campos, y llama al
procedimiento para que se escriban esos valores en la pantalla. Para ello, le pasa por
valor la variable que contiene los datos del alumno.
Construye un proyecto para probar este programa, ejectalo y comprueba que funciona
correctamente. Despus, vuelve a ejecutar paso a paso el programa siguiendo las
instrucciones que ves a continuacin.
Establece un punto de parada en la sentencia strcpy
(alum.nombre, "Juan"); y otro punto de parada en la
sentencia printf ("Nombre: %s\n", a.nombre);
Ejecuta el programa hasta el primer punto de parada.

Coloca en la ventana del watch las variables alum (variable


del programa principal) y a (parmetro formal del
procedimiento). La ventana del watch tendr el aspecto que se
muestra a la derecha. Por una parte se ve que alum es una
estructura (signo + a la izquierda del nombre). Por otra parte, el
computador todava no reconoce la variable a (porque an no
se ha hecho la llamada al procedimiento.
Despliega la estructura alum (clica en el signo +) y ejecuta
paso a paso las tres sentencias del programa principal que
asignan valores a los campos de la estructura. La ventana del
watch mostrar algo parecido a lo que ves a la derecha.
Fijate en el valor del campo nombre. El nmero raro que hay a
la izquierda del valor del campo (Juan) es la direccin de
memoria donde est esa informacin. Escribe es un papel ese
nmero.
Ejecuta ahora hasta el siguiente punto de parada. La ventana
del watch mostrar algo parecido a lo que ves a la derecha
El computador ha entrado en el procedimiento, y ya reconoce
la variable a. Sin embargo, ahora ya no sabe que es la variable
alud, porque pertenece al programa principal que hemos
abandonado temporalmente..
Despliega la estructura a (clica en el signo +). Vers algo como
lo que hay en la derecha.
Observa que los campos de a tienen los mismos valores que los
campos de alum. Observa tambin que la direccin de memoria
en la que est el campo nombre de a es diferente que la
direccin en la que est el campo nombre de alum. Es decir, en
el momento del paso de parmetros, el computador ha hecho
una copia del parmetro en otra posicin de memoria, y el
procedimiento trabaja con esa copia de la informacin.

Vamos a aadir ahora a programa un procedimiento que sube un punto la nota del
alumno si su edad es inferior a 25. El procedimiento es el siguiente:
void sube_nota (Talumno a)
{
if (a.edad < 25)
a.nota = a.nota+1;
}

Incorpora este procedimiento al proyecto, haz una llamada desde el programa principal,
justo antes de la llamada para escribir los datos en la pantalla. Ejecuta despus el
programa y comprueba que NO funciona (debera haber subido un punto a la nota de
Juan).
Para comprender lo que pasa, ejecuta ahora paso a paso el programa y comprueba que
pasa lo siguiente:

1. Dentro del procedimiento sube_nota, el computador efectivamente modifica


el campo nota de la variable a (el parmetro del procedimiento). Pero recuerda
que esa informacin es una copia de los datos originales del programa principal.
2. Acabado el procedimiento, desaparece la variable a (se pierden los cambios), y
el programa principal sigue trabajando con los datos originales que estn en la
variable alum, y que NO han sido modificados por el procedimiento.
3. El programa principal pasa esos datos (por valor) al procedimiento
escribe_datos, que los escribe en la pantalla.
Con todo esto llegamos a la conclusin de que si queremos hacer procedimientos y
funciones que modifiquen el valor de los parmetros, el mecanismo de paso de
parmetros por valor NO nos sirve. Para resolver esta cuestin utilizaremos el
mecanismo de paso de parmetros por referencia.
2

Paso de parmetros por referencia

El cdigo del programa anterior, pero usando ahora el mecanismo de paso por
referencia donde haga falta, es el siguiente:
#include <stdio.h>
#include <string.h>
typedef char Tpalabra [20];
typedef struct {
Tpalabra nombre;
int edad;
int nota;
} Talumno;
void escribe_datos (Talumno a)
{
printf ("Nombre: %s\n", a.nombre);
printf ("Edad: %d\n", a.edad);
printf ("Nota: %d\n", a.nota);
}
void sube_nota (Talumno *a)
{
if (a->edad < 25)
a->nota = a->nota+1;
}
void main ()
{
Talumno alum;
strcpy (alum.nombre, "Juan");
alum.edad = 20;
alum.nota = 6;
sube_nota (&alum);
escribe_datos (alum);
}

Haz los cambios pertinentes en el proyecto, ejecuta la nueva aplicacin y verifica que
funciona correctamente (le sube un punto a la nota de Juan).
El cambio bsico es que ahora el parmetro del procedimiento sube_nota se pasa por
referencia. Los cambios en el cdigo son:

En la cabecera del procedimiento indicamos que el parmetro se pasa por


referencia, poniendo un * antes del nombre del parmetro formal
void sube_nota (Talumno *a)

Al acceder a los campos de la estructura, usaremos una flecha ( -> ) en vez del
punto (.).

En el momento de la llamada al procedimiento, colocamos el signo & antes del


parmetro real.
sube_nota (&alum);

Vuelve a ejecutar ahora el programa paso a paso y comprueba que cuando llamamos a
sube_nota, NO se hace una copia del valor del parmetro (los datos del alumno) sino
que el procedimiento trabaja directamente con los valores originales. Para comprobar
esto, verifica simplemente que la direccin de memoria donde est el campo nombre de
la variable alum es la misma que la direccin donde est el campo nombre del
parmetro a del procedimiento sube_nota. Eso indica que este procedimiento est
accediendo directamente a la informacin original. Por tanto, los cambios que haga
perdurarn incluso cuando acabe el procedimiento. Vuelve a comprobar que cuando
llamamos al procedimiento escribe_datos, esas direcciones de memoria son
diferentes, lo cual indica que se ha hecho una copia y que el procedimiento trabajar con
la copia y no con los datos originales.
Por tanto, cuando un procedimiento o funcin debe modificar la informacin que recibe
como parmetro, debe usarse el mecanismo de paso de parmetros por referencia.
3

Otro ejemplo

Aade ahora el siguiente procedimiento para asignar a la variable alum los datos ledos
del teclado.
void lee_datos (Talumno *a)
{
Tpalabra nom;
int e;
int n;

printf ("Escribe nombre: ");


scanf ("%s",nom);
strcpy (a->nombre,nom);
printf ("\nEscribe edad: ");
scanf ("%d",&e);
a->edad=e;
printf ("\nEscribe nota: ");
scanf ("%d",&n);
a->nota = n;

Lgicamente, el parmetro a debe pasarse por referencia porque vamos a cambiar su


contenido (en concreto, le vamos a asignar a los campos la informacin que leamos del
teclado).
El procedimiento utilizar tres variables locales en las que colocaremos la informacin
leda del teclado, y luego la pasaremos a los campos de la estructura, accediendo a ellos
con el signo ->, puesto que la estructura se recibe por referencia.
Fjate que la asignacin del nombre se hace con la funcin strcpy, porque no pueden
hacerse asignaciones de un vector de caracteres a otro de forma directa.
Fjate finalmente que ahora ya podemos entender porque los parmetros que se le pasan
a la funcin scanf deben estar precedidos por el signo &. Lo que estamos haciendo es
pasar esos parmetros por referencia porque queremos que esa funcin (que pertenece a
la librera de funciones stdio.h) modifique el valor del parmetro (coloque dentro el
valor ledo del techado). Si passemos el parmetro por valor (es decir, scanf
("%d",e)), entonces el valor del parmetro no sera modificado por la funcin scanf,
y el valor ledo del teclado se perdera.
No obstante, observa que en la llamada scanf ("%s",nom); no se usa el smbolo &.
Esto parece contradecir lo que acabamos de decir. Cuando llegues al punto 5 de esta
prctica entenders este detalle.
Aade ahora el procedimiento leer_datos a la aplicacin, inserta una llamada a ese
procedimiento en el punto del programa principal que te parezca ms apropiado y
comprueba que funciona correctamente.
Finalmente, vamos a modificar ligeramente el cdigo del procedimiento leer_datos.
El nuevo cdigo sera el siguiente:
void lee_datos (Talumno *a)
{
printf ("Escribe nombre: ");
scanf ("%s",a->nombre);
printf ("\nEscribe edad: ");
scanf ("%d",&a->edad);
printf ("\nEscribe nota: ");
scanf ("%d",&a->nota);
}

Lo nico que hemos hecho es eliminar las variables locales y colocar los datos que
leemos del teclado directamente en los campos de la estructura. Para eso, pasamos por
referencia a scanf cada uno de los campos de la estructura.
Haz estos cambios en la aplicacin y verifica que todo sigue funcionando
correctamente.
4

Ejercicio

Aade a la aplicacin un procedimiento que debe tener la cabecera siguiente:


void nota_mayor (Talumno *p, Talumno q)
El procedimiento recibe como parmetro dos alumnos, y debe poner en la nota del
alumno p la nota mayor entre la que tiene p y la que tiene q.
5

Haz que el programa principal declare dos alumnos, lea sus datos del terminar, llame al
nuevo procedimiento nota_mayor, y escriba finalmente los datos de los alumnos en
la pantalla, usando el procedimiento escribe_datos.
5

Vectores y matrices

Cuando el parmetro es un vector o una matriz, entonces se pasa SIEMPRE por


referencia, sin necesidad de escribir nada especial (asteriscos, flechas, etc.). Fjate en el
siguiente cdigo.
char primera_letra (Tpalabra p)
{
return p[0];
}

Esta funcin simplemente retorna la primera letra de la palabra que recibe como
parmetro (un vector de 20 caracteres). Aade la funcin al proyecto, y aade en el
programa principal una llamada pasndole como parmetro el campo nombre de un
alumno:
c = primera_letra (alum.nombre);
printf ("%c\n",c);
Ejecuta la aplicacin y verifica que funciona correctamente. Despus ejectala paso a
paso y verifica que la direccin de memoria en la que est el campo nombre del alumno
alum es la misma que la direccin en la que est el parmetro p de la funcin. Es decir,
el parmetro se ha pasado por referencia y no por valor.
Por tanto, cuando el parmetro es un vector o una matriz no hay que preocuparse de si el
paso debe ser por valor o por referencia. Siempre es por referencia.
Comprendes ahora por que en la sentencia
scanf ("%s",a->nombre);

no hay que poner el signo & antes del parmetro?


6

Variables simples

Para acabar, los parmetros consistentes en valores simples (un entero, un real, o un
carcter) tambin se pueden pasar por referencia. Fjate en el ejemplo siguiente:
void dame_datos (Talumno a, int *e, int *nota, char *c)
{
*e = a.edad;
*nota = a.nota;
*c = primera_letra (a.nombre);
}

El procedimiento recibe un alumno por valor, y copia la edad y la nota en dos variables
enteras que recibe como parmetro por referencia, y copia la primera letra del nombre
en una variable de tipo carcter, que tambin recibe como parmetro por referencia.
La llamada a este procedimiento podra ser as:
dame_datos (alum,&e,&n,&c);

Fjate que en la cabecera del procedimiento, para indicar que las variables enteras y el
carcter se pasan por referencia se usa el *. Tambin debe escribirse * antes del nombre
de las variables cuando se accede a ellas dentro del procedimiento. Finalmente, hay que
escribir el signo & antes del nombre de la variable, cuando se hace la llamada al
procedimiento para pasar la variable por referencia.
Incluye estas novedades a tu aplicacin y verifica que funcionan correctamente.
7

Una ultima consideracin

Hemos visto que cuando un procedimiento o funcin debe modificar el valor de un


parmetro, entonces ese parmetro debe pasarse por referencia. Si, por el contrario, no
va a modificarse el valor del parmetro, entonces el parmetro puede pasarse por valor.
No obstante, cuando el parmetro tiene un tamao grande (por ejemplo, en el caso de
una estructura que dentro contiene un vector grande) conviene pasar el parmetro
tambin por referencia, aunque el procedimiento o funcin no vaya a modificar el
parmetro. La razn es que el paso de parmetro por referencia es ms rpido, porque
no hay que hacer una copia del valor del parmetro. Y esa copia puede ser lenta cuando
el parmetro ocupa mucho espacio de memoria.

También podría gustarte