Anda di halaman 1dari 7

El paso de parmetros por referencia

1 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 escribe_datos , que los escribe en la pantalla. valor) al procedimiento

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 correctamente. 4 Ejercicio sigue funcionando

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.

Anda mungkin juga menyukai