Paso de parámetros por valor

Cuando una función solicita los servicios de otra, debe facilitarla los datos que debe procesar. Esta información es enviada a la función a través de unos identificadores o expresiones denominados argumentos o parámetros.

  • Los argumentos enviados en el momento de ser llamada la función se denominan parámetros actuales o reales.

  • Los argumentos recibidos por la función se denominan parámetros formales.

El paso de información se realiza estableciéndose un emparejamiento posicional entre los parámetros formales y los actuales.

Nota

Este método presenta problemas de legibilidad cuando el número de parámetros es grande. Este es un motivo para evitar diseñar funciones que precisen el paso de muchos parámetros (más de cinco, por ejemplo).

En C++ existen dos alternativas para la transmisión de los parámetros a las funciones:

  • Paso por valor

    Los parámetros formales son variables locales a la función, es decir, solo accesibles en el ámbito de ésta. Reciben como valores iniciales los valores de los parámetros actuales. Posteriores modificaciones en la función de los parámetros formales, al ser locales, no afectan al valor de los parámetros actuales.

  • Paso por referencia

    Los parámetros formales no son variables locales a la función, sino alias de los propios parámetros actuales. ¡No se crea ninguna nueva variable! Por tanto, cualquier modificación de los parámetros formales afectará a los actuales.

Traza del paso por valor

La traza de un programa muestra la secuencia de ejecución de las sentencias, además del valor de las variables del programa después de cada acción.

Vamos a analizar la traza de dos ejemplos que usan el paso por valor. De hecho, los ejemplos anteriores vistos hasta el momento han utilizado esta fórmula.

Ejemplo que calcula la media de dos valores (paso 1)

En el primer ejemplo, que calcula una sencilla media de dos valores, supondremos que el usuario introduce los valores 1 y 2.

Se enfatizan las líneas ya ejecutadas del programa.

Estado tras definición de variables en main()

#include <iostream>
using namespace std;

double calcula_media(double num1, double num2)
{
  double media = (num1 + num2)/2.;
  return media;
}

int main()
{
  double numero1, numero2;
  cout << "Introduzca dos números reales: ";
  cin >> numero1 >> numero2;

  double resultado = calcula_media(numero1, numero2);

  cout << "La media es " << resultado << endl;
}
Estado tras definición de variables en main()

Dirección

Valor

Variable

Ámbito

0x6AFE90

0x6AFE98

0x6AFEA8

0x6AFEE8

¿?

resultado

main()

0x6AFEF0

¿?

numero2

main()

0x6AFEF8

¿?

numero1

main()

Los valores ¿? simbolizan el hecho de que el valor es indefinido.

Ejemplo que calcula la media de dos valores (paso 2)

Estado tras introducción de valores en main()

#include <iostream>
using namespace std;

double calcula_media(double num1, double num2)
{
  double media = (num1 + num2)/2.;
  return media;
}

int main()
{
  double numero1, numero2;
  cout << "Introduzca dos números reales: ";
  cin >> numero1 >> numero2;

  double resultado = calcula_media(numero1, numero2);

  cout << "La media es " << resultado << endl;
}
Estado tras introducción de valores

Dirección

Valor

Variable

Ámbito

0x6AFE90

0x6AFE98

0x6AFEA8

0x6AFEE8

¿?

resultado

main()

0x6AFEF0

2.0

numero2

main()

0x6AFEF8

1.0

numero1

main()

Ejemplo que calcula la media de dos valores (paso 3)

Ejecutando función calcula_media() . Estado tras paso por valor.

#include <iostream>
using namespace std;

double calcula_media(double num1, double num2)
{
  double media = (num1 + num2)/2.;
  return media;
}

int main()
{
  double numero1, numero2;
  cout << "Introduzca dos numeros reales: ";
  cin >> numero1 >> numero2;

  double resultado = calcula_media(numero1, numero2);

  cout << "La media es " << resultado << endl;
}
Estado tras paso por valor

Dirección

Valor

Variable

Ámbito

0x6AFE90

2.0

num2

calcula_media()

0x6AFE98

1.0

num1

calcula_media()

0x6AFEA8

¿?

media

calcula_media()

0x6AFEE8

¿?

resultado

main()

0x6AFEF0

2.0

numero2

main()

0x6AFEF8

1.0

numero1

main()

Ejemplo que calcula la media de dos valores (paso 4)

Asignación del valor de media

#include <iostream>
using namespace std;

double calcula_media(double num1, double num2)
{
  double media = (num1 + num2)/2.;
  return media;
}

int main()
{
  double numero1, numero2;
  cout << "Introduzca dos números reales: ";
  cin >> numero1 >> numero2;

  double resultado = calcula_media(numero1, numero2);

  cout << "La media es " << resultado << endl;
}
Estado tras asignación de media

Dirección

Valor

Variable

Ámbito

0x6AFE90

2.0

num2

calcula_media()

0x6AFE98

1.0

num1

calcula_media()

0x6AFEA8

1.5

media

calcula_media()

0x6AFEE8

¿?

resultado

main()

0x6AFEF0

2.0

numero2

main()

0x6AFEF8

1.0

numero1

main()

Ejemplo que calcula la media de dos valores (paso 5)

Asignación del valor devuelto por la función a resultado

#include <iostream>
using namespace std;

double calcula_media(double num1, double num2)
{
  double media = (num1 + num2)/2.;
  return media;
}

int main()
{
  double numero1, numero2;
  cout << "Introduzca dos números reales: ";
  cin >> numero1 >> numero2;

  double resultado = calcula_media(numero1, numero2);

  cout << "La media es " << resultado << endl;
}
Estado tras asignación de resultado

Dirección

Valor

Variable

Ámbito

0x6AFE90

0x6AFE98

0x6AFEA8

0x6AFEE8

1.5

resultado

main()

0x6AFEF0

2.0

numero2

main()

0x6AFEF8

1.0

numero1

main()

Nótese que en el cálculo de resultado, la expresión calcula_media(numero1, numero2) es un valor a la derecha, que recibe el valor devuelto a través del salto incondicional return media de la función.

Ejemplo trampa: incremento del valor de una variable (paso 1)

El siguiente código muestra un ejemplo trampa para confundir al programador novel despistado.

Permite mostrar al alumno el hecho del carácter local de las variables respecto al ámbito de una función.

El fallido propósito de la función incrementa() es el de incrementar en 1 el valor de una variable local a main().

Para cebar la trampa, se utiliza un identicador idéntico para las variable real y formal.

Inicialización de la variable i local a main()

#include <iostream>
using namespace std;

void incrementa(int i)
{
  i = i + 1;
  cout << "Valor incrementado en funcion: " << i << endl;
}

int main()
{
  int i = 5;
  incrementa(i);
  cout << "Valor incrementado en main: " << i << endl;
}
Estado tras inicialización de i en main()

Dirección

Valor

Variable

Ámbito

0x6AFEE0

0x6AFEFC

5

i

main()

Ejemplo trampa: incremento del valor de una variable (paso 2)

Paso por valor a la variable i local a incrementa()

#include <iostream>
using namespace std;

void incrementa(int i)
{
  i = i + 1;
  cout << "Valor incrementado en funcion: " << i << endl;
}

int main()
{
  int i = 5;
  incrementa(i);
  cout << "Valor incrementado en main: " << i << endl;
}
Estado tras paso por valor a la variable i local a incrementa()

Dirección

Valor

Variable

Ámbito

0x6AFEE0

5

i

incrementa()

0x6AFEFC

5

i

main()

Ejemplo trampa: incremento del valor de una variable (paso 3)

Incremento de la variable i local a incrementa()

#include <iostream>
using namespace std;

void incrementa(int i)
{
  i = i + 1;
  cout << "Valor incrementado en funcion: " << i << endl;
}

int main()
{
  int i = 5;
  incrementa(i);
  cout << "Valor incrementado en main: " << i << endl;
}
Estado tras incremento de la variable i local a incrementa()

Dirección

Valor

Variable

Ámbito

0x6AFEE0

6

i

incrementa()

0x6AFEFC

5

i

main()

Ejemplo trampa: incremento del valor de una variable (paso 4)

Salida de la función incrementa()

#include <iostream>
using namespace std;

void incrementa(int i)
{
  i = i + 1;
  cout << "Valor incrementado en funcion: " << i << endl;
}

int main()
{
  int i = 5;
  incrementa(i);
  cout << "Valor incrementado en main: " << i << endl;
}
Estado tras salida de la función incrementa()

Dirección

Valor

Variable

Ámbito

0x6AFEE0

0x6AFEFC

5

i

main()

Como ya se había avanzado, aunque las variables se llaman igual, i, cada una es local al ámbito en el que está declarada.

Esto se observa claramente viendo que están almacenadas en direcciones de memoria diferentes.

La variable local i de incrementa() recibe el valor de la variable local i del main(). Por ello, las modificaciones realizadas sobre i en incrementa() no tienen ningún efecto sobre la variable local i del main().