undefined
Tabla de fichas | Indice del Tema | 0803 |
0801 | 0802 | 0803 | 0804 | 0805 | 0806 | 0807 | 0808 |
![]() Universidad de Salamanca | ![]() Departamento de Informática y Automática | FUNCIONES PASO DE PARÁMETROS © 2006- José R. García-Bermejo Giner |
printf("Hola, Mundo\n");la cadena
"Hola, Mundo\n"
es un parámetro real.
return
en el espacio reservado en 2(a).
#include<stdio.h> void f(int, float); int main(int argc, char * argv) { int entero; float real; entero = 22; real = 33.3; printf("Antes de llamar a f, entero vale %d y real vale %6.2f\n", entero, real); f(entero, real); printf("Después de volver de f, entero vale %d y real vale %6.2f\n", entero, real); return 0; } void f(int p, float q) { printf("Al entrar en f, p (= entero) vale %d y q (= real) vale %6.2f\n", p, q); p = 7777; q = 12345.67; printf("Al salir de f, p vale %d y q vale %6.2f\n", p, q); return; } /* Antes de llamar a f, entero vale 22 y real vale 33.30 Al entrar en f, p (= entero) vale 22 y q (= real) vale 33.30 Al salir de f, p vale 7777 y q vale 12345.67 Después de volver de f, entero vale 22 y real vale 33.30 */
p
y
q
(que son variables locales de la función
f()
) no afectan -no pueden afectar- a los valores de los parámetros reales
entero
y
real
, que son variables locales de la función
main()
. Para ser exactos parámetro real
entero
tiene el valor 22 y el parámetro real
real
tiene el valor 33.3. Al efectuar la llamada a la función, es como si se ejecutase el código siguiente:
p = entero; q = real;y por tanto p toma el valor 22 y q toma el valor 33.3. A continuación, dentro de la función se ejecuta el segmento de código:
p = 7777; q = 12345.67;y cambian los valores de
p
y
q
, según se comprueba mediante la sentencia
printf()
que contiene la función
f()
. Pero al volver a la función
main()
y escribir de nuevo (tras volver de
f()
, según se indica) los valores las variables locales de
main()
, se observa que
no han cambiado los valores de
entero
y
real
. Esto significa, desde un punto de vista "macroscópico", que en C el paso de parámetros está construido de tal manera que el paso de información es unidireccional: la información pasa de la función que hace la llamada (la que aporta parámetros reales) a la función que recibe la llamada (la que tiene los parámetros formales)
pero no en el sentido inverso
. Esto se denomina paso de parámetros por valor. int p; int * q = &p; *q = 33;Como puede verse, hemos modificado el valor del entero
p
a través de un puntero. A todos los efectos,
*q
equivale a
p
, esto es, se puede poner
*q
en cualquier lugar en que aparezca
p
. Esto puede aplicarse al paso de parámetros:
si en lugar de pasar el valor de un parámetro real se pasa la dirección del parámetro real en cuestión, entonces será posible modificar el valor del parámetro real deshaciendo la indirección (anteponiendo un asterisco al puntero) desde el interior de la función
. Considérese el programa siguiente, que es una modificación del empleado en la sección anterior: #include<stdio.h> void f(int *, float *); int main(int argc, char * argv) { int entero; float real; entero = 22; real = 33.3; printf("Antes de llamar a f, entero vale %d y real vale %6.2f\n", entero, real); f(&entero, &real); printf("Después de volver de f, entero vale %d y real vale %6.2f\n", entero, real); return 0; } void f(int *p, float *q) { printf("Al entrar en f, *p (= entero) vale %d y *q (= real) vale %6.2f\n", *p, *q); *p = 7777; *q = 12345.67; printf("Al salir de f, *p vale %d y *q vale %6.2f\n", *p, *q); return; } /* El resultado de ejecutar este programa es el siguiente:
Antes de llamar a f, entero vale 22 y real vale 33.30 Al entrar en f, *p (= entero) vale 22 y *q (= real) vale 33.30 Al salir de f, p vale 7777 y q vale 12345.67 Después de volver de f, entero vale 7777 y real vale 12345.67 */
&entero
y
&real
, respectivamente. Los valores de estos parámetros son las direcciones de
entero
y
real
, respectivamente. Luego al llamar a esta función es como si se ejecutase el siguiente fragmento de código:
p = &entero; q = ℜde modo que
p
pasa a contener la dirección de
entero
y
q
pasa a contener la dirección de
real
. Entonces
*p
equivale por completo a
entero
y
*q
equivale por completo a
real
, puesto que el operador asterisco antepuesto a un puntero deshace la indirección y proporciona la variable señalada por el puntero al que se antepone. Como
*p
equivale a
entero
, poner
*p = 7777
es exactamente lo mismo que poner
entero = 7777
, y análogamente poner
*q = 12345.67
equivale a poner
real = 12345.67
. En resumidas cuentas,
al pasar como parámetros punteros en lugar de valores, se pueden modificar los valores señalados por los punteros (los parámetros reales)
. Esto se denomina
paso por referencia
. #include<stdio.h> #define MAX 10 void crear(int **q); int main(int argc, char * argv[]) { int * p; int i; crear(&p); if (p != NULL) { for(i=0;i<MAX;i++) p[i] = i; for(i=0;i<MAX;i++) printf("p[%d] vale %d\n", i, p[i]); } else printf("\nNo hay memoria suficiente\n"); return 0; } void crear(int **q) { *q = (int *)malloc(MAX*sizeof(int)); } /* p[1] vale 1 p[2] vale 2 p[3] vale 3 p[4] vale 4 p[5] vale 5 p[6] vale 6 p[7] vale 7 p[8] vale 8 p[9] vale 9 */En la llamada a la función
crear()
, el parámetro real es
&p
; este valor se copia en el parámetro formal,
q
. Por tanto, en la llamada a la función es como si se ejecutase el código:
q = &p;que almacena en
q
la dirección de
p
. Entonces, si se antepone un asterisco a
q
, se obtiene precisamente
p
. Luego podemos asignar a
*q
el resultado de una llamada a
malloc()
, que reserva memoria para toda una lista. Los punteros tienen la posibilidad de admitir un índice, y eso es precisamente lo que se hace en el programa principal, una vez asignado a
p
un valor que es la dirección del bloque reservado por
malloc()
.
p
a
crear()
por referencia; esto nos ha permitido modificar el valor de
p
desde dentro de
crear()
, y utilizar entonces el nuevo valor de
p
desde el programa principal. Tipo_base lista[MAX];entonces es como si
lista
fuera equivalente a
&lista[0]
. Esto tiene consecuencias inmediatas cuando se pasa una lista como parámetro a una función:
todas las listas en C pasan por referencia
, porque su nombre equivale automáticamente a la dirección de su primer elemento. Considérese el ejemplo siguiente: #include<stdio.h> #define MAX 20 void leer(char * p); void leer2(char * p); int main(int argc, char * argv[]){ char nombre[MAX]; printf("Escriba un nombre: "); leer(nombre); printf("El nombre que ha escrito es %s\n", nombre); return 0; } void leer(char * p) { fgets(p, MAX, stdin); fpurge(stdin); } void leer2(char * p) { size_t n; fgets(p, MAX, stdin); fgetln(stdin, &n); if (n != 0) { printf("Limpieza...\n\n"); fpurge(stdin); } } /* Escriba un nombre: Miguel de Unamuno y Jugo El nombre que ha escrito es Miguel de Unamuno y */
char
llamada
nombre
a la función
leer()
. Como las listas equivalen a la dirección de su primer elemento, se admite el parámetro real nombre. Al efectuar la llamada a la función, es como si se ejecutase el código siguiente:
p = &nombre[0];
fgets()
y de este modo leemos tantos caracteres como indica
MAX
. La lectura no tiene peligro. Por si acaso, hemos llamado a la función
fpurge()
para eliminar posibles restos del búfer de teclado. La función
leer2()
emite un aviso si realmente había necesidad de "limpieza". struct Registro { char n[40]; float f; int n[10]; };Se pide construir una función que admita argumentos de tipo Registro, y que muestre sus campos en pantalla, con formato en columnado.
struct Registro { char n[40]; float f; int n[10]; };Se pide construir una función que admita parámetros de este tipo y pida los datos de sus campos al usuario, para devolver la estructura, con sus datos ya cumplimentados, a la función que haya hecho la llamada. ¿Cómo pasan las estructuras, por valor o por referencia?
struct Registro { char n[40]; float f; int n[10]; };Se dispone de una lista de estructuras de este tipo, formada por MAX elementos. Se pide construir una función que admita la lista y rellene los campos de todas sus estructuras con unos valores iniciales preestablecidos.
f
.
float
. Construir otras funciones análogas para campos de tipo
cadena
y de tipo
int
. Si hay errores manifiestos, la función lo comunicará a través de un puntero de
int
.
double
. Construir otras funciones análogas para campos de tipo
cadena
y de tipo
int
. Si hay errores manifiestos, la función lo comunicará a través de un puntero de
int
.