Parámetros y argumentos (programación)

De EcuRed
Parámetros y argumentos (programación)
Información sobre la plantilla
Concepto:Un argumento o parámetro es el medio a partir del cual podemos expandir el ámbito de variables locales de funciones, hacia otras funciones y además quienes nos permiten establecer comunicaciones entre funciones

Parámetros y argumentos (programación): Un argumento o parámetro es el medio a partir del cual podemos expandir el ámbito de variables locales de funciones, hacia otras funciones y además quienes nos permiten establecer comunicaciones entre funciones. Si nos vemos ante la necesidad de visualizar o modificar el valor de una variable local en otra función que llamaremos, debemos invocar a dicha función haciendo referencia de su nombre, seguido de los parámetros o nombres de variables para las cuales, en teoría ampliaríamos su ámbito.

Las palabras parámetro y argumento, aunque de significado similar, tiene distintas connotaciones semánticas: Se denominan parámetros los tipos declarados en el prototipo (que deben corresponder con los declarados en la definición). Cuando se realiza una llamada a la función, los "valores" pasados se denominan argumentos. A veces se utilizan también las expresiones argumentos formales, para los parámetros y argumentos actuales para los valores pasados.

 Parámetros (en prototipo o definición)   argumentos formales
 Valores pasados (en tiempo de ejecución)   argumentos actuales

Contenido

Sintaxis

La sintaxis utilizada para la declaración de la lista de parámetros formales es similar a la utilizada en la declaración de cualquier identificador. Ejemplos:

int func(void) {...}                  // sin parámetros
inf func() {...}                      // ídem.
int func(T1 t1, T2 t2, T3 t3=1) {...} // tres parámetros simples,
                                     // uno con argumento por  defecto
int func(T1* ptr1, T2& tref) {...}    // los argumentos son un puntero y
                                     // una referencia.
int func(register int i) {...}        // Petición de uso de registro para
                                     // argumento (entero)
int func(char* str,...) {...}         /* Una cadena y cierto número de otros
            argumentos, o un número fijo de argumentos de tipos  variables */

Los argumentos son siempre objetos. Sus tipos pueden ser: escalares; estructuras; uniones, o enumeraciones; clases definidas por el usuario; punteros o referencias a estructuras y uniones, o punteros a funciones, a clases o a matrices. El tipo void está permitido como único parámetro formal. Significa que la función no recibe ningún argumento. Todos los parámetros de una función tienen ámbito del bloque de la propia función y la misma duración automática que la función.

El único especificador de almacenamiento que se permite es register. En la declaración de parámetros también pueden utilizarse los modificadores volatile y const. Este último se utiliza cuando se pasan argumentos por referencia y queremos garantizar que la función no modificará el valor recibido. Ejemplo:

 int dimension(X x1, const X& x2)      // x2 NO se puede modificar!!

Argumentos por defecto

C++ permite tener valores por defecto para los parámetros. Esto supone que, si no se pasa el parámetro correspondiente, se asume un valor predefinido. La forma de indicarlo es declararlo en el prototipo de la función, como se muestra en el ejemplo (ambas expresiones son equivalentes).

 float mod (float x, float y = 0);
 float mod (float, float = 0);

Un argumento por defecto no puede ser repetido o cambiado en una siguiente declaración dentro del mismo ámbito. Por ejemplo:

void func (int x = 5);
...
void func (int x = 5);    // Error: repetición de argumento por defecto
{                         // nuevo ámbito
   void func (x = 7);    // L.4 Correcto: esta función oculta a la anterior
}                         // el ámbito anterior vuelve a ser visible
void func (x = 7);        // Error: cambiar argumento por defecto

La gramática de C++ exige que los parámetros con valores por defecto deben ser los últimos en la lista de parámetros, y que si en una ocasión falta algún argumento, los que le siguen también deben faltar (adoptar también los valores por defecto). Los argumentos por defecto de métodos (funciones-miembro de clases) no pueden ser otros miembros a no ser que sean estáticos. Los argumentos por defecto no pueden ser otros argumentos:

x = somefunc (int x, int y = x);    // Error!

Los argumentos pasados por referencia solo pueden adoptar valores por defecto, estáticos, globales, o de un subespacio cualificado.

Argumentos: por valor y por referencia

Existen dos formas de pasar argumentos a las funciones: por valor y por referencia. El primero es utilizado por defecto con la declaración usual de parámetros. En el paso "por valor", se crean copias de los argumentos pasados a la función, los cuales, junto a las variables locales (incluyendo el posible valor devuelto), y la dirección de vuelta a la rutina que efectúa la invocación, son pasados a la pila en la secuencia de llamada. Más tarde, cuando termina su ejecución definitivamente, es decir, cuando el control vuelve a la función que la invocó, toda esta información es sacada de la pila mediante la secuencia de retorno (y se pierde). Estos procesos suponen un consumo de tiempo y espacio (memoria), a veces considerable.

Paso por valor

Hemos visto que el paso de parámetros por valor significa que existen copias de los argumentos formales (estas copias son variables locales de la función llamada), y que una función no puede alterar ninguna variable de la función que la invocó.

  • La única excepción es el caso de las matrices. Cuando se utiliza una matriz como argumento en la llamada a una función, el valor pasado es un puntero a la dirección de memoria del principio de la matriz.

Cuando los argumentos pasan por valor pero no hay concordancia entre el tipo de los argumentos actuales y los argumentos formales utilizados en la declaración de la función, entonces se produce un modelado de tipo antes de la asignación.

Pasar un puntero

En C clásico, cuando se desea que la función llamada pueda alterar el valor de variables de la función que la invoca, o ahorrar el espacio que supone la copia local de los argumentos (que pueden ser estructuras de datos muy grandes), la solución consistía en utilizar punteros a las variables respectivas como argumentos para la función (en vez de pasar las variables en sí mismas). A su vez, la función llamada debía declarar el parámetro como puntero, y acceder a la variable indirectamente a través de él. En otras palabras: cuando en C se desea que un valor X pase a una función F y que esta pueda alterar el valor de X en la función que la invocó, el argumento utilizado es &X (la dirección de X). De esta forma, aunque F recibe una copia de &X, puede alterar el valor original a través de esta dirección. Esta técnica puede tener sus ventajas. Por ejemplo, si X es una estructura muy grande, pero puede tener efectos colaterales peligrosísimos y ser una fuente de errores difíciles de detectar.

Paso por referencia

C++ permite utilizar la técnica del C clásico descrita arriba, pero también utilizar el paso de argumentos por referencia (en realidad es una variante semántica del proceso anteriormente descrito). Para ello se utiliza el declarador de referencia &. Las referencias presentan las ventajas de los punteros, en el sentido que permiten modificar los valores de los objetos pasados como argumento, y de que permiten ahorrar espacio si hay que pasar objetos muy grandes, pero no presentan los peligros potenciales de aquellos. En caso necesario las referencias pueden declararse constantes, indicando así que la función invocada no modificará estos valores. En estos casos, la utilización de referencias obedece casi exclusivamente a razones de eficacia en el mecanismo de llamada.

Comparativa

A continuación se muestran tres implementaciones de una misma función; cada una con una forma distinta para paso del argumento.

Implementación-1: Sistema clásico, paso "por valor"

int pru1(int n) {   // n entero; pasa "por valor"
  return 3 * n;
}
...
int x, i = 4;
x = pru1(i);        // ahora: x = 12, i = 4
int& ry = i;
x = pru (ry);       // ahora: x = 12, i = 4

La última sentencia no es un paso por referencia, sino por valor (a pesar de que el argumento actual sea una referencia).

Implementación-2: Sistema clásico, paso de "punteros por valor" (seudo-referencia)

void pru2(int* np) {  // np puntero-a-entero; pasa "por valor"
  *np = (*np) * 3;
}
 . . .
int x = 4;
pru2(&x);             // ahora x = 12

En este caso, pasar el valor &x (dirección de x) como argumento, es equivalente a pasar un puntero a dicha variable (que es lo exigido en la definición de pru2). Es decir, la última línea se puede sustituir por las siguientes:

int* ptr = &x         // define puntero-a-x
pru2(ptr);            // pasa el puntero como argumento

Implementación-3: Sistema C++, paso "por referencia"

void pru3(int& n) { // n tipo "referencia-a-int"; pasa "por  referencia"
  n = 3 * n;
}
 . . .
int x = 4;
pru3(x);            // ahora x = 12

En este último caso, la declaración int& n como parámetro de la función pru3, establece que este n sea declarado como "referencia-a-entero", de forma que cuando se pasa el argumento x, la función crea un valor n que es una especie de alias o espejo de x, de forma que la expresión n = 3*n tiene el mismo efecto que x = 3*x.

En la declaración de una referencia, cuando el iniciador es una constante, o un objeto de tipo diferente que el referenciado, se crea un objeto temporal para el que la referencia actúa como un alias. Esta creación de objetos temporales es lo que permite la conversión de tipos referencia-a-tipoX cuando se utilizan como parámetros de funciones y no hay concordancia entre el valor recibido y el esperado (suponiendo que exista posibilidad de conversión).

Puede Consultar

Fuente